如何正确使用git merge-base检查是否可以执行快进合并?

39

我创建了一个Bash脚本,执行以下任务:

  1. 从上游获取更改;
  2. 检查是否可以进行快进式合并;
  3. 如果#2为真,则将origin/master合并到master
  4. 如果#2为假,则将masterorigin/master之上进行变基。

代码如下:

#!/bin/sh

local_branch=$(git rev-parse --symbolic-full-name --abbrev-ref HEAD)
remote_branch=$(git rev-parse --abbrev-ref --symbolic-full-name @{u})
remote=$(git config branch.$local_branch.remote)

echo "Fetching from $remote..."
git fetch $remote

if git merge-base --is-ancestor $local_branch $remote_branch; then
    echo 'Fast-forward is possible. Merging...'
    git merge --ff-only $remote_branch
else
    echo 'Fast-forward is not possible. Rebasing...'
    git rebase --preserve-merges $remote_branch
fi

我测试了几次,似乎可以正常工作,但我对git merge-base这部分不是非常有信心。我知道关于快进式合并的理论,并且使用masterorigin/master作为merge-base的参数似乎是正确的,但我不是Git专家。

所以我问:这是检查在执行fetch后是否可能进行快进合并的正确方法吗?


为什么不总是使用rebase呢?如果可能的话,rebase会快进。 - Lucas Trzesniewski
1
在我的测试中,执行快进式合并比变基快得多,因此我更愿意在无法进行快进式合并时才进行变基。 - Michael Benford
1
为什么不总是使用git merge --ff-only,只有在失败时才使用rebase呢? - nert
5
那确实是一个可行的选项,但我想知道如何简单检查是否可能出现FF。 - Michael Benford
4个回答

32
git merge-base --is-ancestor <commit> <commit>

--is-ancestor检查第一个<commit>是否是第二个<commit>的祖先,如果是则以状态0退出,否则以状态1退出。非1的非零状态表示出现了错误。

例如:

 git merge-base --is-ancestor origin/master master

1
OP想要处理两个分支,而不是两个提交。如果您可以将<commit>替换为每个分支的HEAD提交的引用,那么这个答案将得到改进。 - Bob Ray
5
状态码可以用 echo $? 进行检查,因此一个手动检查的示例命令可能是 git merge-base --is-ancestor develop myotherbranch && echo $? - lucidbrot
要在Windows上使用PowerShell查看状态代码,请使用echo $lastExitCode(从dbsnet.fr学到的)。因此,git merge-base --is-ancestor branch1 branch2; echo $lastExitCode - Scott H
4
@BobRay,你可以将分支名称放在<commit>的位置,然后Git会将其解析为该分支HEAD所指向的提交。 - Scott H
2
@lucidbrot 我认为如果第一个命令失败,&& 将不起作用。因此,也许这样会稍微好一点:git merge-base --is-ancestor develop myotherbranch; echo $? - TTT

8

您想使用--ff-only选项。

git merge origin/master --ff-only

如果可以快速前进,它会自动发生。 如果不可能,则该命令将失败,并显示fatal: Not possible to fast-forward, aborting.,然后可以运行重新基于命令。


这是否意味着这是完全回滚?我正在编写一个脚本,从上游获取了更改,然后检查了已跟踪文件的未提交更改,接着检查是否有任何未跟踪的文件。现在我想看看是否会覆盖任何未跟踪的文件。我认为进行合并的干运行就足够了。我认为这涵盖了我所有的情况? - mazunki

2

如果您实际执行合并操作,您就会确定!使用临时分支,以便您不会在其他分支中搞糟任何东西。

git fetch
git checkout -b tmp_branch master
git merge origin/master

你的脚本需要在此处评估合并结果,并根据情况将origin/master合并到master中,或者不进行合并。
只需在不需要时删除临时分支即可。
git branch -D tmp_branch

4
谢谢你的回答。这种方法可能有效,但是“merge-base”更为直接,因为它只需要计算两个提交之间的合并基础即可立即得出答案。 - Michael Benford
我使用上面的例子,但是用 git merge --ff-only origin/master。它绝对有效,并且比 merge-base 命令更容易记忆。 - Bob Ray

2
检查$remote_branch是否为$local_branch的祖先命令是:
test -z "$(git rev-list --max-count 1 $local_branch..$remote_branch)"

如果没有--max-count 1,则会列出所有在$remote_branch中但不在$local_branch中的提交记录。由于您只关心是否存在这样的提交记录并且无论如何都要丢弃输出,因此可以添加--max-count 1以加快速度。


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