如何实现 `git --no-ff --ff-only`,而这是不允许的。

24
作为我们频繁变基的工作流的一部分,我希望在主分支上使用合并。特别是当主题分支已经变基到最新的主提交时,我想仅合并此时任何合并都是快进式的。我可以通过使用以下命令来实现: git merge --ff-only 此外,我想记录主题分支与空提交的集成,这就是我想使用--no-ff的原因,因为它强制执行了这种行为: git merge --no-ff 不过,我真正想要的是两者的结合:仅在情况简单时合并,但仍然让我记录它。Git认为: fatal: You cannot combine --no-ff with --ff-only. 这似乎对某些人来说是显而易见的。 git merge --edit --no-ff topicbranch 也无法实现我想要的行为。那么,如果涉及的主题分支已经变基到最新的主提交,我该如何使用--no-ff选项进行合并?
更新:看起来Charles Bailey的答案会解决问题。如果您希望将其合并为一个git别名,可以使用此处发布的内容:
git config --global alias.integrate '!test "$(git merge-base HEAD "$1")" = "$(git rev-parse HEAD)" && git merge --no-ff --edit $1 || echo >&2 "Not up-to-date; refusing to merge, rebase first!"'

有点啰嗦但是行得通。请注意使用选项--edit编辑提交信息的影响力。


--ff-only 在标题中被使用。我会修改文本以使其更清晰。我正在使用 --ff-only 作为检查,以确定合并是否是微不足道的。@CharlesBailey,我发现你对下投票相当慷慨。唉唉。 - DrSAR
3
基本上你不想使用 --ff-only,因为你确实想要创建一个合并提交。你可以将此检查作为单独的步骤执行:例如,test $(git merge-base HEAD topicbranch) = $(git rev-parse HEAD) && git merge --no-ff topicbranch - CB Bailey
你能将这个转化为别名并放入答案中吗?我会给你点赞的。 - DrSAR
1
仅供参考,这里有一些讨论可用此处:"然而,如果今天重新设计Git合并,我可以看到可以将它们设计为两个正交开关。" - Jaakko Luttinen
1
如今,在完成拉取请求时,希望这样做已经变得很常见了。我列出了一些最受欢迎的工具,它们提供了这个功能,被称为Azure Devops中的Semi-Linear Merge。详细信息可以在此答案的附注中找到。 - TTT
3个回答

13

你不需要使用--ff-only,因为你想要创建一个合并提交,而--ff-only会阻止这一操作。

你可以在运行合并命令之前进行单独的检查。你可以将此封装成一个简单的shell函数。

例如:

merge_if_ahead () {
  if test "$(git merge-base HEAD "$1")" = "$(git rev-parse HEAD)"; then
      git merge --no-ff "$1"
  else
      echo >&2 "Not up to date; refusing to merge"
      return 1
  fi
}

1
你好,你有没有想过在Bitbucket或Github上强制使用它?使用Git Hooks可以实现吗? - Raphael Oliveira

2
git config alias.mff '!mff() { git merge --ff-only "$1" && git reset --hard HEAD@{1} && git merge --no-ff "$1"; }; mff'

从那时起,以后
git mff other-branch

这与@andreas-wederbrand的答案有何不同? - DrSAR
1
哦,实际上我忽略了他的解决方案。但是既然你问了,我的解决方案不会启动一个新的 sh 可执行文件,而只是定义了一个被调用的函数。这样可以节省启动另一个进程的成本。 - Vampire

2
我已经创建了一个别名,可以在我的简单测试中使用。
git config --global alias.ffmerge '!sh -c "git merge --ff-only $1 && git reset --hard HEAD@{1} && git merge --no-ff $1" -'

这个命令由三个依次执行的指令组成,只要第一个指令失败,后面的指令就会自动终止。下面分别解释:

git merge --ff-only $1

如果合并无法继续进行快进合并,则会失败。($1是作为参数传递的分支名称)

git reset --hard HEAD@{1}

如果第一个命令成功,那么意味着我们可以进行快进合并,但是由于我们想要进行合并提交,所以我们将回到成功合并之前HEAD的状态。HEAD@{1}是一个reflog指针。
git merge --no-ff $1

现在,即使合并可以作为快进进行,我们也将使用合并提交进行真正的合并。
只需调用git ffmerge即可完成整个过程,唯一可能失败的步骤是第一步,但这会使我们保持与之前相同的工作副本。
如同使用git reset --hard一样,工作副本应该是干净的。

硬重置将丢弃所有未暂存的更改。在实际需要检查的情况下,这是不必要和危险的。(即,您只需要检查是否可能进行快进,但现在您应该,但不检查是否存在已暂存或未暂存的更改。) - CB Bailey
@Andreas Wederbrand 谢谢您。尽管硬重置看起来有些冒险,但它对我的目的很有效(而且不像 -z 解决方案那样依赖于 shell 问题)。为了补偿其他人的猛烈投票习惯,您将得到一个 +1。尽管我会尝试将 bereal 的解决方案转换为工作别名。 - DrSAR
我同意 reset --hard 可能会很危险,这就是为什么我提到工作副本应该是干净的。这种合并通常会发生在某种合并机器上,其他事情都不会发生。 - Andreas Wederbrand

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