如何修复多个错误的合并?

时间:2014-02-26 07:44:29

标签: git merge git-merge git-rebase git-checkout

我们的Git存储库中有一个相当复杂的情况。我们有两个团队在使用同一个软件。一个团队正在对3.0版本进行维护,而另一个团队正在进行一个长期运行的项目,这将导致3.1版本很快就会发布。

3.1团队开始在他们自己的分支上,他们决定合并3.0分支的所有更改,每次从3.0分支发布新版本时,通常每隔一周发布一次。因此,当3.0团队重新发布他们的第一个版本时,提交了R1并将其合并到3.1中,从而产生了M1。

  3.1---o---o---M1
 /             /
3.0---o---o---R1

两周后,3.0团队在R2发布了他们的第二个版本,而3.1团队将其合并,从而产生了M2。

  3.1---o---o---M1---o---o---M2
 /             /            /
3.0---o---o---R1---o---o---R2

这已经持续了一段时间,但最终3.0团队进行了重构,并在提交R3中发布了这个。在这次重构中,3.0团队将一个大型Constants.java文件拆分为两个较小的文件:Constants.javaMoreConstants.java。 (这些不是实际的文件名。)Constants.java文件先前已被两个团队更改并成功合并。然而,当他们尝试将R3合并到他们的分支中时,这种重构导致了3.1团队的冲突。 3.1团队决定他们并不想解决这个冲突,他们认为他们可以推迟#34;无视重构,坚持自己的版本解决问题。这不仅意味着他们没有将MoreConstants.java添加到他们的存储库,而且他们也没有合并现在导入MoreConstants.java而不是Constants.java的文件中的更改}。他们做的是:

$ git merge 3.0
$ git status

然后,对于每个列出的冲突,他们决定是否要解决它。如果他们不想解决冲突,他们确实

$ git checkout --ours <file they wanted to ignore>
$ git add <file they wanted to ignore>

这导致在3.1分支上提交C3:

  3.1---o---o---M1---o---o---M2---o---o---C3
 /             /            /            /
3.0---o---o---R1---o---o---R2---o---o---R3

从那时起,他们每次都在做着#--ours&#34; -trick,因为他们每次都希望忽略冲突。同样重要的是要注意3.1团队不断对Constants.java - 文件进行更改。所以随着时间的推移,情况就变成了这样:

  3.1---o---o---M1---o---o---M2---o---o---C3---o---o---C4---o---o---C5
 /             /            /            /            /            /
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5

现在两个分支都有Constants.java的不同版本,有一个共享祖先,这是在R2之前的某个地方的提交。此时,我们可以通过运行

来查看该祖先是什么
$ git merge-base --all C5 R5

现在是复杂的部分。 3.1团队即将完成创建3.1版本的产品。 3.0团队将接管。 3.1发布后,3.0团队将立即发布3.2。因此,3.0分支创建了一个新的3.2分支:

  3.1---o---o---M1---o---o---M2---o---o---C3---o---o---C4---o---o---C5
 /             /            /            /            /            /
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5
                                                                   \
                                                               3.2  A5

当然,版本3.2需要包含3.0和3.1的所有更改。所以我们做了合并:

$ git checkout 3.2
$ git merge 3.1

导致:

  3.1---o---o---M1---o---o---M2---o---x3--C3---o---o---C4---o---o---C5
 /             /            /            /            /            /  \
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5   \
                                                                   \    \
                                                               3.2  A5---A6

现在令人讨厌的惊喜:3.0团队在A6提交中缺少大量修复。事实证明,所有缺失的东西都可能与Constants.java - 重构有关。当然,3.2中缺少新的MoreConstants.java,但3.2分支上缺少导入新MoreConstants.java的文件的大量更改。

在阅读How to revert a merge which used strategy=ours?How to revert a faulty merge(特别是ADDENDUM)后,我想我明白为什么会这样:3.1团队通过选择&#34;我们的&#34;解决了很多冲突。 。这样,事实上他们告诉Git,3.0中的冲突变化是错误的&#34;。因此,正如上述文件中所提到的那样,这实际上是某种形式的恢复合并。不是整个合并都被还原,而只是它的一部分。

这两个文件都包含很多关于如何解决这个问题的线索。但是,两者都专注于修复一个错误合并。在这个简化的示例中,我们讨论的是三个错误合并。实际上,我猜,我们有15到20个错误的合并。

我知道3.1团队不应该忽略冲突,但他们确实如此。所以我们必须找到一个解决方案。到目前为止我唯一能想到的是:

1。很多樱桃采摘

我们可以在3.1行上挑选每一个提交到3.2行,除了合并的提交。听起来很简单,但是由于我们在现实中有大约20个错误的合并,合并之间的提交比简化图中显示的要多得多,我们正在讨论数百个提交的提交。手动执行此操作非常繁琐且容易出错。我认为这个解决方案只有在生成所有提交列表的方法才能生效,并自动将该列表提供给(系列)git命令。

2。很多折扣

一系列连续的rebase。鉴于错误合并的数量,这仍然容易出错且乏味,但可能比之前的建议少一点。在专业方面,这可能会导致更清晰的历史。我认为这个过程如下:

$ git checkout x3
$ git rebase --no-ff 3.0

  3.1---o---o---M1---o---o---M2---o---x3--C3---o---o---C4---o---o---C5
 /             /            /            /            /            /  \
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5   \
 \                                                                 \    \
  \                                                            3.2  A5---A6
   \
    3.1'--o--o--M1'--o--o--M2'---o---x3'

$ git merge R3

  3.1---o---o---M1---o---o---M2---o---x3--C3---o---x4--C4---o---o---C5
 /             /            /            /            /            /  \
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5   \
 \                                      \                          \    \
  \                                      \                     3.2  A5---A6
   \                                      \
    3.1'--o--o--M1'--o--o--M2'---o---x3'---D3

$ git checkout x4
$ git rebase --no-ff R3

  3.1---o---o---M1---o---o---M2---o---x3--C3---o---x4--C4---o---o---C5
 /             /            /            /            /            /  \
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5   \
 \                                      \ \                        \    \
  \                                      \ C3'--o--x4'         3.2  A5---A6
   \                                      \     
    3.1'--o--o--M1'--o--o--M2'---o---x3'---D3   

$ git merge D3

  3.1---o---o---M1---o---o---M2---o---x3--C3---o---x4--C4---o---x5--C5
 /             /            /            /            /            /  \
3.0---o---o---R1---o---o---R2---o---o---R3---o---o---R4---o---o---R5   \
 \                                      \ \                        \    \
  \                                      \ C3'--o--x4'--D4     3.2  A5---A6
   \                                      \             /
    3.1'--o--o--M1'--o--o--M2'---o---x3'---D3----------o   

Etcetera ......

3。生成补丁

第三种解决方案可能是生成&#34;补丁&#34;,仅包含某些提交之间的更改。当我们再一次想象树时:

  3.1---o---o---M1---o---o---M2--y2--o--x3--C3--y3--o--x4--C4--y4--o--x5--C5
 /             /            /              /              /              /  
3.0---o---o---R1---o---o---R2--o---o--o---R3--o---o---o--R4--o---o---o--R5   
                                                                         \    
                                                                     3.2  A5

这意味着创建包含以下&#34;范围&#34;:

中所做的所有更改的修补程序
  • [y2 - x3]
  • [y3 - x4]
  • [y4 - x5]

然后,我们可以在A5之上应用这些补丁,而不是进行合并。

问题

所以这些是我的问题:

  1. 这个问题的最佳解决方案是什么。它可能是(一个变体)我提出的解决方案之一,完全不同的东西。
  2. 有没有办法以(或多或少)自动方式执行解决方案1 ​​,以防止错误并加快速度?
  3. 解决方案2 中的草图是否正常运行?或者我在这里错过了什么?
  4. 解决方案3 怎么样?是否有办法使该过程自动化?

1 个答案:

答案 0 :(得分:1)

  

我们可以在3.1行上挑选每一个提交到3.2行,除了合并的提交。 ...我认为这个解决方案只有在生成所有提交列表的方法才有可行,以便自动将该列表提供给(系列)git命令。

您可以将一系列提交传递给cherry-pick,例如git cherry-pick 16234..351be,您还可以指定-m 1仅跟随第一个父项,这允许您通过合并提交正确传递,因此您不需要跳过它们。当你在每一步解决合并冲突时,它们应该在未来解决,所以希望你只是在每个冲突步骤中遇到常规的合并冲突,事情将更加自动化并且不易出错。