不确定这是否是重复项,但花了将近一天的时间试图找出一个整洁的解决方案,但仍未找到答案。
我的git repo状态现在是这样的:
C---D---E feature-branch
/
A---B---C---D---X---F master
|
Reverts C and D
我一直在研究feature-branch
,并且C
和D
的提交被意外合并以掌握(复杂的故事)。其他正在执行该回购协议的人会还原那些提交,因为它破坏了构建。
现在,在提交E
之后,我想用当前的母版重新建立功能分支。但是问题在于,由于提交C
和D
已经存在于主服务器中,因此在重新定基础时这些提交不会被包括在内。
在git手册页中:
如果上游分支已经包含您所做的更改(例如,因为您邮寄了在上游应用的补丁程序),则该提交将被跳过。例如,根据以下历史记录运行git rebase master(其中A'和A引入了相同的更改集,但是提交者信息不同):
A---B---C topic / D---E---A'---F master
将导致:
B'---C' topic / D---E---A'---F master
由于更改已通过提交C
恢复,我现在如何包括提交D
和X
(可能带有新的SHA)?
答案 0 :(得分:2)
tl; dr:您可以使用
这样的命令来完成所需的操作git rebase --onto master feature-branch~3 feature-branch
(在示例中,feautre-branch~3
起作用,但通常是任何可以提交B
的表达式-例如B
的提交哈希,等等)
要了解其工作原理,请继续阅读...
首先,我们应该澄清一下您的图表。你说
我一直在进行功能分支,并且 C和D的提交被偶然地合并到母版(复杂的故事)。其他正在执行该回购协议的人会还原那些提交,因为它破坏了构建。
(添加了强调)。现在,合并可以完成以下两项操作之一[1],但是复制提交C
和D
并不是其中之一。默认行为如下所示:
A -- B -- C -- D -- !CD -- F <-(master)
\
E <-(feature-branch)
由于master
在意外合并时显然位于B
,因此默认行为是将master
的“快进”到现有D
上承诺。好像feature-branch
之后才创建D
手。
(我也做了一些符号更改。!CD
表示合并了C
和D
的合并(不需要注释)。而且我发现这种画线的方式更具可读性。但是重点是C
和D
的表示方式...)
如果您使用了--no-ff
选项(或者如果master
在合并之前与feature-branch
分叉,则另一种可能性看起来更像
C - D -- E <--(feature-branch)
/ \
A -- B ----- M -- !CD -- F <--(master)
再次merge
将不会重复C
或D
;相反,它会创建一个“合并提交” M
,将对C
和D
的更改合并到master
的历史记录中(并使C
和{{1 }}从D
“可达”。
在两种情况下,合并后master
和feature-branch
之间的“合并基数”为master
。因此,您必须解决两个问题:docs描述的“重复补丁ID”问题;并且合并基础处于排除提交的位置-因为D
甚至不会考虑复制上游已经可以到达的提交。[2]
通常,仅解决“合并基础”问题很有用。最后,我们将了解为什么您不必出于您的目的,但是为了完整起见,这是您的做法:
首先找到一个解析为提交rebase
的表达式(例如上述示例中的B
或B
的提交)。在类似
feature-branch~3
这将复制提交git rebase -f feature-branch~3 feature-branch
,C
和D
,以便您拥有
E
(其中 E
/
A -- B -- C -- D -- !CD -- F <-(master)
\
C' -- D' -- E' <-(feature-branch)
在技术上仍然存在,但无法访问,因此最终可能会被E
破坏)。当然,这是假定“快进”的情况。如果您有合并提交,则看起来会有所不同,但结果将是相同的。
结果是,您现在可以毫无问题地将gc
合并到feature-branch
中。但是,如果您想master
rebase
到feature-branch
(为快速前进做准备),则仍然必须解决您指出的原始问题:master
和C'
将作为D'
和C
的重复项被跳过。
您想要的是阻止修补程序ID重复检查;虽然似乎没有实际的选择,但是您可以通过让git知道从哪里寻找重复项来做到这一点。与其将D
作为上游,不如将提交master
作为上游;然后重新设置B
的值。
--onto master
发出此命令时,不仅git rebase --onto master feature-branch~3 feature-branch
没有理由考虑git
和C
,而且还因为您不再使用D
作为上游,从master
到master
的合并基数不再重要。您明确地说feature-branch
是您的上游,因此这可以立即解决两个问题-这就是为什么如上所述,您最终不必担心B
命令。
[1]实际上,它至少可以做第三件事。如果您指定了rebase -f
选项,则它不会进行真正的合并,而是会在--squash
上创建一个新的提交CD
。这与通常创建的合并提交基本相同,不同之处在于它不包含master
作为父提交。
D
这在某些特定情况下很有用,但是通常我不建议您这样做,因为git“失去了跟踪” C - D -- E <--(feature-branch)
/
A -- B ----- CD -- !CD -- F <--(master)
和C
在{{ 1}}。在您的情况下,这会很好地解决-这就是为什么我知道您不是这样做的原因,并且在答案的正文中没有包含这种可能性。但是在大多数情况下,这会使分支之间的未来交互更加困难。
[2]这些问题看似相似,但截然不同。在一种情况下,D
本身已经可以从您所要迁移的上游开始到达;在另一种情况下,不是,但是另一个引入相同更改的提交是
答案 1 :(得分:1)
您可以使用git cherry-pick。在您的特定示例中,该命令将为:
in master# git cherry-pick topic~3..topic
答案 2 :(得分:1)
在功能分支中执行
git rebase master
,它将通过忽略C D来基于母版和您期望的结果。(功能分支中的A---B---C---D---X---F--E
)
然后执行git reset --soft <sha of F>
暂时摆脱提交E。
接下来git stash
重置提交更改(即E)
然后在同一feature-branch
中执行以下操作,
git cherry-pick <sha of C>
git cherry-pick <sha of D>
最后git stash pop
并做git commit
,以便您取回E。
这将达到您的预期。
我在本机中进行了测试,下面是git log --online
在feature-branch
中获得的结果
ae4b6bb E commited
eee0a6a D commited
3141961 C commited
a44aa27 F Commited
265da4c Commited X
9e2729d D commited
84a3a9b C commited
8a543ca B commited
4e4a8ca A commited