使用合并的提交重新设置Git

时间:2018-07-05 14:47:21

标签: git github merge repository rebase

不确定这是否是重复项,但花了将近一天的时间试图找出一个整洁的解决方案,但仍未找到答案。

我的git repo状态现在是这样的:

      C---D---E  feature-branch
     /
A---B---C---D---X---F  master
                |
         Reverts C and D

我一直在研究feature-branch,并且CD的提交被意外合并以掌握(复杂的故事)。其他正在执行该回购协议的人会还原那些提交,因为它破坏了构建。

现在,在提交E之后,我想用当前的母版重新建立功能分支。但是问题在于,由于提交CD已经存在于主服务器中,因此在重新定基础时这些提交不会被包括在内。

在git手册页中:

  

如果上游分支已经包含您所做的更改(例如,因为您邮寄了在上游应用的补丁程序),则该提交将被跳过。例如,根据以下历史记录运行git rebase master(其中A'和A引入了相同的更改集,但是提交者信息不同):

      A---B---C topic
     /
D---E---A'---F master
     

将导致:

               B'---C' topic
              /
D---E---A'---F master

由于更改已通过提交C恢复,我现在如何包括提交DX(可能带有新的SHA)?

3 个答案:

答案 0 :(得分:2)

tl; dr:您可以使用

这样的命令来完成所需的操作
git rebase --onto master feature-branch~3 feature-branch

(在示例中,feautre-branch~3起作用,但通常是任何可以提交B的表达式-例如B的提交哈希,等等)

要了解其工作原理,请继续阅读...


首先,我们应该澄清一下您的图表。你说

  

我一直在进行功能分支,并且 C和D的提交被偶然地合并到母版(复杂的故事)。其他正在执行该回购协议的人会还原那些提交,因为它破坏了构建。

(添加了强调)。现在,合并可以完成以下两项操作之一[1],但是复制提交CD并不是其中之一。默认行为如下所示:

A -- B -- C -- D -- !CD -- F <-(master)
                \
                 E <-(feature-branch)

由于master在意外合并时显然位于B,因此默认行为是将master的“快进”到现有D上承诺。好像feature-branch之后才创建D手。

(我也做了一些符号更改。!CD表示合并了CD的合并(不需要注释)。而且我发现这种画线的方式更具可读性。但是重点是CD的表示方式...)

如果您使用了--no-ff选项(或者如果master在合并之前与feature-branch分叉,则另一种可能性看起来更像

       C - D -- E <--(feature-branch)
      /     \
A -- B ----- M -- !CD -- F <--(master)

再次merge将不会重复CD;相反,它会创建一个“合并提交” M,将对CD的更改合并到master的历史记录中(并使C和{{1 }}从D“可达”。

在两种情况下,合并后masterfeature-branch之间的“合并基数”为master。因此,您必须解决两个问题:docs描述的“重复补丁ID”问题;并且合并基础处于排除提交的位置-因为D甚至不会考虑复制上游已经可以到达的提交。[2]

通常,仅解决“合并基础”问题很有用。最后,我们将了解为什么您不必出于您的目的,但是为了完整起见,这是您的做法:

首先找到一个解析为提交rebase的表达式(例如上述示例中的BB的提交)。在类似

的命令中使用它
feature-branch~3

这将复制提交git rebase -f feature-branch~3 feature-branch CD,以便您拥有

E

(其中 E / A -- B -- C -- D -- !CD -- F <-(master) \ C' -- D' -- E' <-(feature-branch) 在技术上仍然存在,但无法访问,因此最终可能会被E破坏)。当然,这是假定“快进”的情况。如果您有合并提交,则看起来会有所不同,但结果将是相同的。

结果是,您现在可以毫无问题地将gc合并到feature-branch中。但是,如果您想master rebasefeature-branch(为快速前进做准备),则仍然必须解决您指出的原始问题:masterC'将作为D'C的重复项被跳过。

您想要的是阻止修补程序ID重复检查;虽然似乎没有实际的选择,但是您可以通过让git知道从哪里寻找重复项来做到这一点。与其将D作为上游,不如将提交master作为上游;然后重新设置B的值。

--onto master

发出此命令时,不仅git rebase --onto master feature-branch~3 feature-branch 没有理由考虑gitC,而且还因为您不再使用D作为上游,从mastermaster的合并基数不再重要。您明确地说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 --onlinefeature-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