Git:从母版中删除先前合并的提交

时间:2018-07-24 16:00:34

标签: git version-control

我正在使用的存储库使用Git的标记功能来处理master分支中的“发布”。

我刚刚被要求从master中删除与先前发行版相关的提交,但是如果将来需要将它们提交回master,也将这些提交保留在另一个分支上

这是一个发布历史记录的示例:

  1. 版本-v3(最新)
  2. release-v2(最新倒数第二个)
  3. release-v1(最新第三)

其中release-v2包含需要从master中删除的提交。

最终,我认为我想发生的事情是:

master包含: 1.版本-v3 2. release-v1

preserved-branch包含保留的提交: 1. release-v2

我不确定如何最好地执行此请求。我最终是否需要--force推翻master的当前状态?

2 个答案:

答案 0 :(得分:3)

基本上有两种方法可以解决此问题。每个都有优点和缺点。

因此,让我们绘制一个简单的近似当前状态图。根据您的问题标题,我推断该工作已在分支上完成,master由这些分支的合并提交组成。如果是这样,它可能看起来像这样:

  A -- B    C    D -- E
 /      \  / \  /      \
O ------ M1 - M2 ------ M3 <--(master)
         ^    ^         ^
         v1   v2        v3

现在,您想更新master,以便它包含来自C的更改,但是您希望保留这些更改,以防您需要未来。请注意,如果C中的更改与以后的提交中的更改重叠,则每种方法都将涉及一定数量的冲突解决方案。

使用rebase

一种选择是重写历史记录。该选项将涉及--force(或最好是--force-with-lease)推送到master上,进而需要清理每个人的本地克隆,以避免撤消重写。另外,由于存在合并提交,因此这样做很复杂。

第一步是创建新分支;在更改任何其他内容之前最容易做到的。因此,请检查落实C。您将需要一个可解析为提交C的表达式-提交ID是您可以使用的最通用的东西,但是在我们的示例中,您还可以使用类似master^^2(第1个父级的第二个父级) master提示提交的父项。

git checkout master^^2
git branch v2-archive

现在有

              C <--(v2-archive)
             / \
  A -- B    /   \    D -- E
 /      \  /     \  /      \
O ------ M1 ----- M2 ------ M3 <--(master)
         ^        ^         ^
         v1       v2        v3

剩下的就是重写master。这里的想法是rebase,但是rebase与合并提交不能很好地融合。

您可以尝试使用--prseerve-merges选项。如果使用默认的合并设置成功完成了M3(或更笼统地说,正在重写的每个合并),那么可能会没事。该命令看起来像

git rebase --preserve-merges --onto master~2 master^ master

在我们的示例中,master~2master^master表示为解析为M1M2M3的表达式。如果可行,这会让您无聊

  A -- B    /- D' -- E'
 /      \  /           \
O ------ M1  ---------- M3' <--(master)
         ^ \
         v1 \-- C  <--(v2-archive)
             \   \
              \-- M2 -- D -- E
                  ^  \        \
                  v2  \------- M2 
                               ^
                               v3

其中D'E'是将更改从DE重放到v1提交的结果,并且M3'合并了返回到master(实际上是对M3的重写)。请注意,所有原始提交仍然存在,尤其是标签仍指向始终指向的位置。

现在,您必须决定如何处理标签v3。通常的惯例是标签不动。我的建议是创建一个新的版本号,并相应地标记M3'。然后,您可以选择删除v2v3标签,或只是出于存档目的而保留它们。

如果操作不顺利,则选择另一个选项(从

开始
              C <--(v2-archive)
             / \
  A -- B    /   \    D -- E
 /      \  /     \  /      \
O ------ M1 ----- M2 ------ M3 <--(master)
         ^        ^         ^
         v1       v2        v3
再次

),将是在E提交时创建一个临时分支

git checkout master^2
git branch temp

然后重新建立临时分支的基础

git rebase --onto master~2 master^ temp

所以你有

              C <--(v2-archive)
             / \
  A -- B    /   \    D -- E
 /      \  /     \  /      \
O ------ M1 ----- M2 ------ M3 <--(master)
         ^ \      ^         ^
         v1 \     v2        v3
             \
              D' -- E' <--(temp)

接下来,您必须将master推回到M1,然后在temp分支中合并

git checkout master
git reset --hard master~2
git merge temp
git branch -D temp

请小心考虑最初在M3中出现的任何非标准合并行为。最终结果基本相同。这只是确保正确处理合并的一种更手动的方法。您仍然需要像以前一样决定如何处理标签。

无论哪种方式,这时您都已完成重写。 D'E'M3'都是未经测试的代码状态,理想情况下,应该对它们全部进行验证,以确保它们通过了自动化测试。当然,M3' 必须进行重新测试,但是如果您希望像bisect这样的调试工具在将来能正常工作,那么还应该确保重写的中间状态是“干净”。

然后您可以进行强制推入并开始清理。

使用revert

另一种选择是使用git revert。这样可以保留历史记录。因此,如果真正的目标是使该产品的下一版本排除v2功能,那也许很好;但是如果要从历史记录中真正删除更改是字面上的要求,那就不是这样。

最大的优势是,无需重写历史记录,就不会对团队中的每个人施加压力,也不会从上游的基础恢复。通常,这应该是更简单的过程。不利的一面是,您必须采取额外的步骤,以便将来重新集成来自v2的更改。

所以我们回到了初始状态

  A -- B    C    D -- E
 /      \  / \  /      \
O ------ M1 - M2 ------ M3 <--(master)
         ^    ^         ^
         v1   v2        v3

同样,我们在C放置了一个分支。

git checkout master^^2
git branch v2-archive

但是随后我们继续

git revert -m1 master^

请注意-m1选项;这告诉git从其“第一父级”的角度撤消合并M2-即撤消C的更改。 (在示例中,您可以只还原C而不是M2;但是如果还原的分支确实有很多提交,那么还原合并将更简单。)

现在您拥有了

              C <--(v2-archive)
             / \
  A -- B    /   \    D -- E
 /      \  /     \  /      \
O ------ M1 ----- M2 ------ M3 -- !M2 <--(master)
         ^        ^         ^
         v1       v2        v3

其中!M2撤消master引入M2的所有更改。与历史记录重写一样,您需要确定如何处理标签。在这种情况下,也许更清楚为什么v2v3最好单独保留(或只是删除),所以您可能想为!M2创建一个新的版本号。 / p>

但是现在,如果您尝试将v2-archive合并到master中,git会发现所有内容都是最新的,因为C-您尝试重新整合的提交-已从master“到达”(通过父指针)。因此,git文档说恢复合并意味着您永久放弃分支中的更改;但有一种解决方法。

git rebase -f v2-archive^ v2-archive

这将创建一个C的新副本,该副本与C基本相同,但是无法从master到达

              C' <--(v2-archive)
             / 
  A -- B    /- C    D -- E
 /      \  /    \  /      \
O ------ M1 ---- M2 ------ M3 -- !M2 <--(master)
         ^       ^         ^
         v1      v2        v3

答案 1 :(得分:1)

我将从版本2的提交中创建反向提交,并将其检入master。

这将使您有机会解释正在发生的事情(您正在撤消的更改)。

这也避免了重写历史记录和--forcing