Git:检查分支是否严格是另一个分支的后代

Ken*_*ams 4 git git-merge

我看到了一些关于“如何检查分支是否X已重新基于Y”的问题,但我还没有找到具有我想要的特定风味的分支。

我想检查是否X是“简单的分支下降Y”。X严格来说,只允许在和 之间使用这种模式,而不是在和Y之间使用这种模式:ZY

换句话说,如果分支中的每个提交都X具有Y作为祖先(或者等于Y或是其中一个Y祖先)——而不仅仅是 的尖端是否具有作为X祖先Y

这对于帮助人们实现基于 rebase 的合并模式是可取的,其中X是功能分支,Y是主要开发分支。(如果有人真的知道他们在做什么,并且非严格分支是他们真正想要的,我们可以轻松地授予特殊豁免并允许合并。但我们希望他们至少意识到他们正在这样做,这一直没有发生。)

请注意,这与“可以合并”不同--ff-onlygit merge --ff-only会很高兴地更新Y以指向XifY是 的后代X,即使 的某些祖先X既不是 的后代也不是祖先Y

我知道我可以遍历所有祖先X并检查是否Y是每个祖先的祖先(当我到达Y自身时停止,并确保没有多个父母),但我想知道是否有更好的内置东西到我错过的 Git。

附录:

pull正如 @TTT 指出的,另一个常见的情况是有人在 之前使用 ing而push不是执行 ,从而搞乱了 rebase push --force-with-lease。我为我们的内部“如何使用 Git”文档创建了下图,如果能自动执行此检查就太好了:

LeG*_*GEC 5

检查所有提交是否Y..X都是 的后代的一种方法是使用或Y检查该范围的边界。git log --boundary Y..Xgit rev-list --boundary Y..X

从这段历史开始:

$ git log --graph --oneline --all
* 036a9f9 (HEAD -> X) create d.txt
* cadd199 create c.txt
| * 0680934 (Z) Merge commit '22a23fe' into Z
|/| 
| * 22a23fe create b.txt
* | 8dec744 (Y) create a.txt
|/  
* 878ac8b first commit
Run Code Online (Sandbox Code Playgroud)

你会得到 :

$ git log --oneline --boundary Y..X
036a9f9 (HEAD -> X) create d.txt
cadd199 create c.txt
- 8dec744 (Y) create a.txt   # <- one single boundary commit, pointing at Y

$ git log --oneline --boundary Y..Z
0680934 (Z) Merge commit '22a23fe' into Z
22a23fe create b.txt
- 8dec744 (Y) create a.txt   # <- two commits on the boundary
- 878ac8b first commit       # <-
Run Code Online (Sandbox Code Playgroud)

检查您是否处于这种情况的可编写脚本的方法是:

# 'git rev-list' prints full hashes, boundary commits are prefixed with '-'
boundary=$(git rev-list --boundary Y..X | grep -e '^-')

want=$(git rev-parse Y)
want="-$want"

# the boundary should consist of "-<hash of Y>" only:
if [ "$boundary" = "$want" ]; then
   echo "all commits in X are descendants of Y"
fi
Run Code Online (Sandbox Code Playgroud)

上面只是检查所有提交是否都在Y. 您还可能面临以下情况:

* 036a9f9 (HEAD -> X) create d.txt
* 0680934 Merge 'origin/X' into X # <- someone created a merge commit in between
|\
| * cadd199 create c.txt
* | 22a23fe create b.txt
|/
* 8dec744 (Y) create a.txt
* 878ac8b first commit
Run Code Online (Sandbox Code Playgroud)

这也会影响变基工作流程。

如果您还想排除这种情况,请使用或--merges选项:git loggit rev-list

# git rev-list also has a --count option, which will output the count
# rather than the complete list of commits
merges=$(git rev-list --count --merges Y..X)
if [ "$merges" -eq 0 ]; then
   echo "all good, no merges between Y and X"
fi
Run Code Online (Sandbox Code Playgroud)

的文档--boundary没有很好地解释什么是“边界提交”。

我想说这个答案有一个不错的定义:

边界提交是限制修订范围但不属于该范围的提交。例如,修订范围 HEAD~3..HEAD 由 3 个提交(HEAD~2、HEAD~1 和 HEAD)组成,并且提交 HEAD~3 作为其边界提交。

更正式地说,git 通过从指定的提交开始并通过父链接获取其他提交来处理修订范围。它在不满足选择标准的提交处停止(因此应该被排除) - 这些是边界提交。