我正在使用的存储库使用Git的标记功能来处理master
分支中的“发布”。
我刚刚被要求从master
中删除与先前发行版相关的提交,但是如果将来需要将它们提交回master
,也将这些提交保留在另一个分支上
这是一个发布历史记录的示例:
其中release-v2
包含需要从master
中删除的提交。
最终,我认为我想发生的事情是:
master
包含:
1.版本-v3
2. release-v1
preserved-branch
包含保留的提交:
1. release-v2
我不确定如何最好地执行此请求。我最终是否需要--force
推翻master
的当前状态?
答案 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~2
,master^
和master
表示为解析为M1
,M2
和M3
的表达式。如果可行,这会让您无聊
A -- B /- D' -- E'
/ \ / \
O ------ M1 ---------- M3' <--(master)
^ \
v1 \-- C <--(v2-archive)
\ \
\-- M2 -- D -- E
^ \ \
v2 \------- M2
^
v3
其中D'
和E'
是将更改从D
和E
重放到v1
提交的结果,并且M3'
合并了返回到master
(实际上是对M3
的重写)。请注意,所有原始提交仍然存在,尤其是标签仍指向始终指向的位置。
现在,您必须决定如何处理标签v3
。通常的惯例是标签不动。我的建议是创建一个新的版本号,并相应地标记M3'
。然后,您可以选择删除v2
和v3
标签,或只是出于存档目的而保留它们。
如果操作不顺利,则选择另一个选项(从
开始 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
的所有更改。与历史记录重写一样,您需要确定如何处理标签。在这种情况下,也许更清楚为什么v2
和v3
最好单独保留(或只是删除),所以您可能想为!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
。