这就是gitk目前在我们的一个项目中的表现:
https://dl.dropbox.com/u/2582508/gitk.png
显而易见,我们可以说,在一个“git merge”与远程分支完成之后发生了 - 我们不确定为什么或者发生了什么。知道这里发生了什么吗?
更重要的是,解决这个问题的最佳方法是什么?那些合并提交是空的,但是当做“git rebase -i”时,合并提交通常似乎没有出现。
最重要的是,我宁愿不让历史与其他克隆不兼容,即它们仍然应该能够拉/推/合并它。这甚至可能吗?
答案 0 :(得分:2)
这是有人使用git pull
(或等效地,git fetch
和git merge
)更新项目的结果。想象一下,你的存储库看起来像这样:
o---o---o [origin/master] \ A---B---C [master]
也就是说,您已完成了对原始回购中的提交A
,B
和C
的提交。
与此同时,其他一些人会进行更改并将其推送到共享存储库。如果然后运行git fetch
,您的存储库将如下所示:
o---o---o---D---E---F [origin/master] \ A---B---C [master]
现在,如果您运行git merge
(请记住:git pull
仅为git fetch
,后跟git merge
)。你会有这个:
o---o---o---D---E---F [origin/master] \ \ A---B---C---G [master]
假设一切顺利,G
可能只是一个“愚蠢的”合并提交;从技术上讲,G
的状态与F
和C
不同,因此必须采用不同的方式。
现在,如果您推动此更改,您将拥有以下内容:
o---o---o---D---E---F \ \ A---B---C---G [origin/master]
如果你继续开发,你会得到这个:
o---o---o---D---E---F \ \ A---B---C---G [origin/master] \ H---I---J [master]
现在,如果你继续这样做(如果很多人继续这样做),你最终会得到一张像你图片中那棵树的树。这不是“错误的”,但许多人不喜欢它,因为它使开发历史很难遵循。
解决这个问题的方法是教人们改变。 Rebasing将(如你所述)远程无用的合并提交并给你一个更清晰的历史记录。在上面的例子中,您将得到一个线性发展历史。这更容易遵循。但是,您需要知道在重新绑定后需要重新构建并重新测试代码...仅仅因为代码合并很容易并不意味着结果是正确的。
如果您正在使用中央存储库来共享官方开发线,则可以实现预接收挂钩,以检测这些自动(“愚蠢”/“无用”)合并提交并拒绝推送 - 强制用户要么变坏。实际上,您希望钩子查找默认的合并提交消息...因此,如果您确实希望保持合并提交(有时这是有意义的),您必须至少编写一个智能提交消息。
这是一个示例钩子。我剥掉了一堆额外的东西,所以我没有去测试它。
#!/bin/bash # This script is based on Gnome's pre-receive-check-policy hook. # This script *only* checks for extraneous merge commits. # Used in some of the messages server=git.wherever.com GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) in_import() { test -e "$GIT_DIR/pending" } forced() { test -n "$GNOME_GIT_FORCE" } check_commit() { commit=$1 subject="$(git log $commit -1 --pretty=format:%s)" if expr "$subject" : ".*Merge branch.*of.*\(git\|ssh\):" > /dev/null 2>&1; then if ! in_import && ! forced ; then cat &2 --- The commit: EOF git log $commit -1 >&2 cat &2 Looks like it was produced by typing 'git pull' without the --rebase option when you had local changes. Running 'git pull --rebase' now will fix the problem. Then please try, 'git push' again. Please see: http://live.gnome.org/Git/Help/ExtraMergeCommits --- EOF exit 1 fi fi } check_ref_update() { oldrev=$1 newrev=$2 refname=$3 change_type=update if expr $oldrev : "^0\+$" > /dev/null 2>&1; then change_type=create fi if expr $newrev : "^0\+$" > /dev/null 2>&1; then if [ x$change_type = xcreate ] ; then # Deleting an invalid ref, allow return 0 fi change_type=delete fi case $refname in refs/heads/*) # Branch update branchname=${refname#refs/heads/} range= # For new commits introduced with this branch update, we want to # run some checks to catch common mistakes. # # Expression here is same as in post-receive-notify-cia; we take # all the branches in the repo, as "^/ref/heads/branchname", other # than the branch we are actualy committing to, and exclude commits # already on those branches from the list of commits between # $oldrev and $newrev. if [ -n "$range" ] ; then for merged in $(git rev-parse --symbolic-full-name --not --branches | \ egrep -v "^\^$refname$" | \ git rev-list --reverse --stdin "$range"); do check_commit $merged done fi ;; esac return 0 } if [ $# = 3 ] ; then check_ref_update $@ else while read oldrev newrev refname; do check_ref_update $oldrev $newrev $refname done fi exit 0