这是 gitk 目前在我们的一个项目中的样子:
https://dl.dropbox.com/u/2582508/gitk.png
显然,据我们所知,这发生在使用远程分支完成单个“git merge”之后 - 我们不确定为什么或发生了什么。知道这里发生了什么吗?
更重要的是,修复它的最佳方法是什么?这些合并提交是空的,但是在执行“git rebase -i”时,合并提交通常似乎不会出现。
最重要的是,我不想让历史与其他克隆不兼容,即他们应该仍然能够拉/推/合并它。这甚至可能吗?
这是 gitk 目前在我们的一个项目中的样子:
https://dl.dropbox.com/u/2582508/gitk.png
显然,据我们所知,这发生在使用远程分支完成单个“git merge”之后 - 我们不确定为什么或发生了什么。知道这里发生了什么吗?
更重要的是,修复它的最佳方法是什么?这些合并提交是空的,但是在执行“git rebase -i”时,合并提交通常似乎不会出现。
最重要的是,我不想让历史与其他克隆不兼容,即他们应该仍然能够拉/推/合并它。这甚至可能吗?
这是有人使用git pull(或,等效地,git fetch和git merge)更新他们的项目的结果。想象一下,您的存储库如下所示:
o---o---o [起源/主人]
\
A---B---C [大师]
也就是说,您已经完成了 commits A,B并且C在原始仓库中的内容之上。
与此同时,其他人进行更改并将其推送到共享存储库。如果您随后运行git fetch您的存储库,则如下所示:
o---o---o---D---E---F [起源/主人]
\
A---B---C [大师]
现在,如果你运行git merge(请记住:git pull后面git fetch跟着git merge)。你会有这个:
o---o---o---D---E---F [起源/主人]
\ \
A---B---C---G [大师]
假设一切顺利,G可能只是一个“愚蠢的”合并提交;从技术上讲,状态G是不同的F,C因此必须以不同的方式考虑。
现在,如果你推动这个改变,你会得到这个:
o---o---o---D---E---F
\ \
A---B---C---G [起源/主人]
如果你继续开发,你会得到这个:
o---o---o---D---E---F
\ \
A---B---C---G [起源/主人]
\
H---I---J [大师]
现在,如果您继续这样做(并且如果很多人继续这样做),您最终会得到一棵像您图片中的树一样的树。这不是“错误的”,但很多人不喜欢它,因为它使开发历史非常难以遵循。
解决这个问题的方法是教人们rebase。变基将(如您所述)远程无用的合并提交并为您提供更清晰的历史记录。在上面的例子中,你最终会得到一个线性的发展历史。这更容易遵循。但是,您需要知道,在 rebase 之后,您需要重新构建和重新测试您的代码......仅仅因为代码容易合并并不意味着结果是正确的。
如果您使用中央存储库来共享官方开发线,您可以实现一个 pre-receive 钩子来检测这些自动(“愚蠢”/“无用”)合并提交并拒绝推送 - 强制用户要么变基. 实际上,您希望钩子查找默认的合并提交消息......所以,如果您确实想保留合并提交(有时这是有道理的),您至少必须编写一个智能提交消息。
这是一个示例钩子。我去掉了一堆额外的东西,所以我没有去测试它。
#!/bin/bash
# 这个脚本基于 Gnome 的 pre-receive-check-policy 钩子。
# 这个脚本*仅*检查无关的合并提交。
# 在一些消息中使用
服务器=git.wherever.com
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
in_import() {
测试 -e "$GIT_DIR/待定"
}
强迫(){
测试 -n "$GNOME_GIT_FORCE"
}
check_commit() {
提交=$1
主题="$(git log $commit -1 --pretty=format:%s)"
if expr "$subject" : ".*Merge branch.*of.*\(git\|ssh\):" > /dev/null 2>&1; 然后
如果 !in_import && !强迫; 然后
猫 &2
---
提交:
EOF
git log $commit -1 >&2
猫 &2
看起来它是通过键入没有 --rebase 的 'git pull' 生成的
当您进行本地更改时的选项。现在运行 'git pull --rebase'
将解决问题。然后请再次尝试“git push”。请参见:
http://live.gnome.org/Git/Help/ExtraMergeCommits
---
EOF
1号出口
菲
菲
}
check_ref_update() {
旧版本=$1
新版本=$2
参考名=$3
更改类型=更新
if expr $oldrev : "^0\+$" > /dev/null 2>&1; 然后
更改类型=创建
菲
if expr $newrev : "^0\+$" > /dev/null 2>&1; 然后
如果 [ x$change_type = xcreate ] ; 然后
# 删除无效的引用,允许
返回 0
菲
更改类型=删除
菲
案例 $refname 在
裁判/头/ *)
# 分支更新
分支名称=${refname#refs/heads/}
范围=
# 对于此分支更新引入的新提交,我们希望
# 运行一些检查以发现常见错误。
#
# 这里的表达式和 post-receive-notify-cia 中的一样;我们采取
# repo 中的所有分支,如“^/ref/heads/branchname”,其他
# 比我们实际提交的分支,并排除提交
# 已经在提交列表中的那些分支上
# $oldrev 和 $newrev。
如果 [ -n "$range" ] ; 然后
对于合并在 $(git rev-parse --symbolic-full-name --not --branches | \
egrep -v "^\^$refname$" | \
git rev-list --reverse --stdin "$range"); 做
check_commit $合并
完毕
菲
;;
经社理事会
返回 0
}
如果 [ $# = 3 ] ; 然后
check_ref_update $@
别的
同时读取 oldrev newrev refname;做
check_ref_update $oldrev $newrev $refname
完毕
菲
出口 0