这是 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