在合并时跳过特定的提交

250
我现在已经使用Git大约一年了,觉得它非常棒,但我刚刚开始了项目的第二个版本,并为此创建了一个新的分支。我在如何处理接下来的事情方面有些困惑。
我有两个分支,一个是master10(用于v1),另一个是master20(用于v2)。我一直在master10分支上进行v1的错误修复,并在master20上开发新的功能。每当我进行错误修复时,我会切换到master20分支,然后执行git merge master10命令将其合并到v2中。到目前为止一切顺利。
然而,现在我在v1中做了一个我不想在v2中出现的更改,但我仍然希望继续合并其他错误修复。我该如何告诉Git跳过这个特定的提交(或一系列提交),但在未来仍然希望合并其他错误修复呢?
我以为git rebase可能是我需要的,但是阅读了文档后,我几乎被绕晕了。
我想我需要的是类似于"git sync"命令,告诉Git这两个分支现在是同步的,并且在未来只合并从这个同步点开始的提交。
非常感谢任何帮助。
8个回答

343
如果您想要将"maint"分支上的大部分但不是全部提交合并到"master"分支,那么可以这样做。需要一些工作--- 如上所述,通常情况下是合并分支上的所有内容--- 但有时可能会发生这样的情况:您对一个发布版本进行了更改,但不应该将其集成回来(也许该代码已经在master中被取代),那么如何表示呢?具体步骤如下:
假设“maint”分支已经应用了5个更改,并且其中一个更改(maint~3)不应该合并回“master”,尽管其他所有更改都应该合并。您需要完成三个步骤:实际合并该提交之前的所有内容,告诉git即使它没有合并也将maint~3标记为已合并,然后合并剩余的内容。关键步骤如下:
bash <master>$ git merge maint~4
bash <master>$ git merge -s ours maint~3
bash <master>$ git merge maint

第一条命令将你烦恼的maint提交之前的所有内容合并到master分支中。默认的合并日志信息会告诉你正在合并“branch 'maint' (early part)”。

第二条命令合并了烦恼的maint~3提交,但选项“-s ours”告诉git使用一种特殊的“合并策略”,实际上是通过保持你正在合并的树并完全忽略你正在合并的提交来工作的。但它仍然会生成一个新的合并提交,其中HEAD和maint~3是父提交,因此修订图现在称maint~3已经被合并了。所以实际上你可能还想使用-m选项来解释maint~3提交实际上是被忽略的!

最后一条命令将maint(maint~2..maint)的剩余部分合并到master中,以便再次同步。


6
如果您需要推迟合并该提交,那么您别无选择,只能跳过它(使用"merge -s ours"),然后稍后使用"cherry-pick"命令进行应用。一旦该提交从主分支可以到达,就不能再次合并-避免重复合并相同的更改是git的主要目标之一。 - araqnid
3
关于您提到的子分支示例:我猜想,一旦您合并了子分支,您的情况就与我在文章中提到的第二步之后的情况完全相同。创建额外的分支对提交的关系没有影响。 - araqnid
5
仅出于兴趣,如果您只想省略一个更改,为什么不仅执行一次合并,然后撤销那一个变更集,而不是执行三次合并?这将导致提交到存储库的变更集更少,并且历史更加明确。 - Mark Booth
4
通常将提交以明确的名称命名会更容易。因此,您只需要查看要合并的分支的 git 日志,并注意不应该合并的提交哈希和前一个提交哈希 - 而不是计算提交数。 - zwirbeltier
20
你想要跳过的提交可能与你想要合并的分支存在巨大的冲突。直接跳过它比修复冲突、撤销更改、再次解决冲突更容易。 - SztupY
显示剩余15条评论

55

在我看来,最合乎逻辑的做法是将所有内容合并,然后使用git revert (commit_you_dont_want)来移除它。

示例:

git merge master
git revert 12345678

如果您有多个要忽略的提交,或者想要编辑还原消息:

git merge master
git revert -n 123456
git revert -n abcdef
git commit -m "... Except commits 123456 and abcdef"

那么你的历史记录可能像这样:

| ... Except 123456 and abcdef
|\ Merge branch 'master' into 'your_branch'

如果您只涉及到这些“to-ignore”提交的冲突,您可以使用:

git merge master -X ours

所以你的版本将会覆盖其他版本。即使没有错误信息,你仍然可以“还原”那些不需要的提交,因为它们可能有其他更改并且你仍然不想要它们。

如果你遇到不仅仅涉及“要忽略”的提交的冲突,你应该手动解决它们,而且在还原时可能还需要再次解决它们。


3
如果您之后想将这些已还原的提交合并到相关分支中,Git 会继续忽略它们吗? - Anriëtte Myburgh
1
@AnriëtteMyburgh 你可以恢复回滚的提交,然后再合并它们——这是使用“merge -s ours”策略不能轻松实现的。 - Lily Chung
这对我来说比之前的答案更有效。 - Meeting Attender
这对我来说就是答案。感谢您提醒我回滚存在的意义。我正在使用Unity并保留了两个具有不同项目设置的分支(必须被跟踪);这种方法是在早期阶段保持2个单独的存储库的好替代方案。唯一的缺点是每次合并时都必须记住回滚(以及哪些提交)。 - Steak Overflow
如果您稍后将合并的分支合并回源分支,则这些已还原的提交也将被合并,这是不可取的。因此,根据已接受的答案,在那时您需要跳过这些还原操作。尽管还原操作很容易区分,但这种方法可能比仅使用已接受的答案更容易。 - ivan_pozdeev
显示剩余3条评论

20

提交包括祖先. 如果没有合并之前的提交,就无法合并提交.

当您有一个正在维护模式下的分支时,您可以使用cherry-pick。这是一种不错的流程。


1
谢谢,精选会完成任务。虽不如我所期望的那样好,但还可以。 - Brad Robinson

8

2
听起来像是一个经典的案例,适合使用“top”命令 :D - luca
这与“cherry-pick”无关... - Enerccio

3

这是一种关于我的项目的广告,基本上包含了@araqnid所描述的过程。

这是一个助手,介绍了以下GIT工作流:

  • 每天/每周通知有待合并的维护分支到dev/master分支
  • 分支维护者检查状态并决定是否需要所有提交,或者阻止其中一些提交,或者要求开发人员自行阻止。最后将维护分支合并到上游中。

来自项目页面的引用:

根据工作流程,除了主分支,还可以有维护或客户特定的分支。这些分支也称为LTS分支。 通常,热修补程序会进入报告错误的分支,然后将提交合并回主分支。 一般做法是使所有分支与主分支完全同步,即您希望看到特定分支和主分支之间的明显差异,以了解主分支是否包含所有功能和错误修复。 但是,有时您不想要特定的提交,因为它们是特定于客户的,其他用户不应该看到。或者您的主分支分歧太大,需要完全不同的方法来解决问题,甚至更好的方法是问题已经不存在了。 此外,在从主分支cherry-pick到维护分支的情况下,生成的提交应在主分支中被阻止。

3
您可以使用 git-cherry pick 来实现这一点:
git cherry-pick $(git merge-base HEAD origin/branch)..$(git rev-parse <offending-commit-hash>^)
git cherry-pick <offending-commit-hash>..$(git rev-parse origin/branch)

该命令从当前的HEADorigin/branch的最后一个公共提交开始挑选所有提交,直到要忽略的提交(请注意^告诉 cherry-pick 在该提交的父级处停止)。
第二个命令会挑选从<offending-commit-hash>提交哈希值开始的每个提交与其余的origin/branch
请注意,如果需要合并其他补丁,则仍可能需要进行一些调整(git将停止 cherry-pick 并告诉您解决冲突)。

1

针对这种情况,你需要让 Git 认为你跳过的更改比你所做的更改要旧,而不是使用 revertcherry-pick

因此:

  1. 合并在你想要跳过的提交之前的最后一个提交。这当然会合并所有之前的提交。git merge ccc
  2. 合并你想要跳过的提交。git merge fff --no-commit
  3. 暂存任何合并,取消暂存所有更改,撤消所有更改。(也许有一些绝对可靠的命令可以完成这个步骤,但我会在 UI 中完成这个部分 - 无论你如何知道)
  4. 完成空合并 git merge --continue
  5. 合并你想要跳过的提交之后的提交。git merge source-branch-head

在第四步之后,Git 将认为你的分支比那个提交更新,因为你已经处理过它了(通过选择保留你自己的版本)。


1
创建第三个分支,用于在master10中进行更改,但不在master20中进行更改。始终将master10视为您的“主分支”,即最稳定的分支。所有其他分支都希望随时与其保持同步。

我猜那可能行得通,但我已经处于这种状态了,再加上第三个分支可能会更让我困惑。 :) - Brad Robinson

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