奇怪的git合并问题

4

以下是我们项目中 gitk 的当前外观:

https://dl.dropbox.com/u/2582508/gitk.png

从我们能了解的所有信息来看,这显然发生在使用 "git merge" 与远程分支合并后 - 我们不确定为什么或出了什么问题。你知道这里发生了什么吗?

更重要的是,最佳修复方法是什么?这些合并提交是空的,但在执行 "git rebase -i" 时,通常不会出现合并提交。

最重要的是,我希望不使历史记录与其他克隆不兼容,即它们仍应该能够进行拉取/推送/合并。这是否可能?


我会放弃这个克隆版本,并再次克隆另一个副本。你在这个克隆版本中有任何“未备份的分支/提交”吗? - lprsd
你们是通过中央代码库进行共享的吗?也就是说,你们都向一个公共代码库推送和拉取代码吗? - Pat Notz
是的,有一个共同的代码库,并且进一步的工作已经在那里完成了。 - miracle2k
1个回答

3
这是某人使用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]
现在,如果您继续这样做(如果许多人都这样做),则最终将得到类似于图片中的树形结构。这不是“错误”的,但许多人不喜欢它,因为它使开发历史记录非常难以跟踪。
解决此问题的方法是教导人们如何进行变基。变基会删除无用的合并提交,以便获得更干净的历史记录。在上面的情况下,您将获得线性的开发历史记录。那更容易跟踪。但是,您需要知道,在变基后,您需要重建和重新测试代码...仅因为代码容易合并并不意味着结果是正确的。
如果您正在使用中央仓库共享官方开发线,您可以实现一个 pre-receive 钩子来检测这些自动(“愚蠢”/“无用”)的合并提交并拒绝推送--强制用户重新基于最新版本。实际上,您希望钩子查找默认合并提交消息...因此,如果您确实想保留合并提交(有时这是有意义的),则必须至少编写一个智能提交消息。
以下是一个示例钩子。我删除了一堆额外的东西,所以我没有测试过它。
#!/bin/bash
# 这个脚本基于 Gnome 的 pre-receive-check-policy 钩子。 # 这个脚本只检查多余的合并提交。
# 一些消息中使用 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 <
EOF git log $commit -1 >&2 cat >&2 <
看起来是使用 'git pull' 命令产生的,但没有使用 --rebase 选项, 并且当时有本地更改。现在运行 'git pull --rebase' 可以解决问题。 然后再次尝试 'git push'。请参见:
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 # 删除无效的引用,允许 return 0 fi change_type=delete fi
case $refname in refs/heads/*) # 分支更新 branchname=${refname#refs/heads/}
range= # 对于此分支更新引入的新提交,我们想运行一些检查以捕获常见错误。 # # 此处的表达式与 post-receive-notify-cia 中相同;我们获取存储库中的所有分支, # 格式为 "^/ref/heads/branchname",除了我们正在提交到的分支,并从 $oldrev 和 $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

谢谢你的回答。然而,我要么不理解它,要么我的问题表述不清。我了解合并提交,并且我很乐意在中央仓库中使用它们 - 我认为我们不想在这里使用rebase。但是在上面的截图中,我有30个后续合并,每个合并都有两个父级:直接前一个合并和一系列旧提交中的一个。并没有30个develop-merge-develop步骤。虽然我发现很难理解这些树形结构,但这些合并对我来说似乎也是不必要的。一个(或几个)合并就可以实现你所描述的目标。 - miracle2k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接