这是某人使用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