75

我们有以下情况:

             A --- B --- C --- ... --- iphone
           /
  ... --- last-working --- ... --- master

在 last-working 和 iPhone 之间,进行了 32 次提交。在 last-working 和 master 之间,进行了很多提交。

我现在想要的是一个新的分支,我将 iphone 和当前的 master 合并在一起。并且在稍后的某个时间,这应该被合并到主控中。

首先,我打算这样做:

git checkout iphone -b iphone31
git merge master

但后来我想,如果这样做会更好:

git checkout master -b iphone31
git merge iphone

现在我想知道。结果会有什么不同?合并的行为会有所不同吗?

我已经尝试了这两种方法,正如我所料,我遇到了很多冲突,因为与 master 相比,iphone 真的很旧。现在我想知道合并它们的最简单方法。

也许甚至从 master 开始并将每个 iphone 提交合并到它会更容易?像这样做:

git checkout master -b iphone31
git merge A
git merge B
git merge C
...
git merge iphone

最后,当这个合并完成时(即所有冲突都已解决并且它正在工作),我想这样做:

git checkout master
git merge iphone31
4

5 回答 5

48

关于备选方案

git checkout iphone -b iphone31
git merge master

git checkout master -b iphone31
git merge iphone

他们会有相同的轻松或困难,就像争论一个杯子是半满的还是半空的。

版本树感知

我们如何看待版本树在某种程度上只是我们任意的看法。假设我们有一个版本树,如下所示:

    A----------+
    |          |
    |          |
   \_/        \_/
    B          X
    |          |
    |          |
   \_/        \_/
    C          Y
    |
    |
   \_/
    D
    |
    |
   \_/
    E

假设我们想要根据从 C 到 E 的更改从 Y 签出一个新版本 Z,但不包括从 A 到 C 所做的更改。

“哦,那会很困难,因为没有共同的起点。” 不是真的。如果我们只是像这样在图形布局中稍微不同地放置对象

      /
    C+---------+
    | \        |
    |          |
   \_/         |
    D          B
    |         / \
    |          |
   \_/         |
    E          A
               |
               |
              \_/
               X
               |
               |
              \_/
               Y

现在事情开始看起来很有希望了。请注意,我在这里没有更改任何关系,箭头都指向与上图相同的方向,版本 A 仍然是公共基础。仅更改布局。

但现在想象一棵不同的树是微不足道的

    C'---------+
    |          |
    |          |
   \_/        \_/
    D          B'
    |          |
    |          |
   \_/        \_/
    E          A
               |
               |
              \_/
               X
               |
               |
              \_/
               Y

任务就是正常合并版本 E。

因此,您可以合并任何您想要的东西,唯一影响难易程度的是在您选择作为起点或公共基础的位置与您合并到的位置之间所做的更改的聚合。您不仅限于使用版本控制工具建议的自然起点。

对于某些版本控制系统/工具,这可能并不简单,但如果所有其他方法都失败了,则没有什么可以阻止您手动执行此操作,方法是签出版本 C 并将文件另存为 file1,签出版本 E 并将文件另存为 file2 ,检出版本 Y 并将文件另存为 file3,然后运行kdiff3 -o merge_result file1 file2 file3​​.

回答

现在,对于您的具体情况,很难准确地说出哪种策略会产生最少的问题,但是如果有很多更改会产生某种冲突,那么拆分和合并较小的部分可能会更容易。

我的建议是,由于 last-working 和 iphone 之间有 32 次提交,因此您可以例如从 master 的分支开始,然后合并前 16 次提交。如果结果太麻烦,请还原并尝试合并 8 个第一次提交。等等。在最坏的情况下,您最终会一一合并 32 个提交中的每一个,但这可能比必须在一个合并操作中处理所有累积的冲突更容易(在这种情况下,您正在使用一个非常不同的代码库) .

提示:

在纸上画出版本树,并用箭头记下您要合并的内容。如果您将流程分成几个步骤,请在完成时划掉。这将使您更清楚地了解您想要实现的目标、到目前为止所做的事情以及剩下的事情。

我真的可以推荐KDiff3,它是一个出色的差异/合并工具。

于 2010-02-28T15:35:11.063 回答
35

它们是有区别的。


始终在合并期间检出目标分支(即,如果将 A 合并到 B,则检出 B)。


人们常说合并方向无所谓,但这是错误的。尽管无论合并方向如何,生成的内容都是相同的,但有几个方面是不同的:

  1. 结果合并提交中列出的差异将根据方向而有所不同。
  2. 大多数分支可视化器将使用合并方向来决定哪个分支是“主要的”。

为了详细说明,想象一下这个夸张的例子:

  • 您在 1000 次提交后从 MASTER 分支出来,并将其命名为 DEVELOP(或者在跟踪分支场景中,您已经有一段时间没有获取了)。
  • 您将一个提交添加到 DEVELOP。您知道此更改没有冲突。
  • 您想将更改推送到 MASTER。
  • 您错误地将 MASTER 合并DEVELOP(即 DEVELOP 在合并期间被签出)。然后,您将 DEVELOP 作为新的 MASTER 推送。
  • 结果合并提交中的差异将显示在 MASTER 中发生的所有 1000 次提交,因为 DEVELOP 是参考点。

这些数据不仅无用,而且很难阅读正在发生的事情。大多数可视化工具会让你的 DEVELOP 线看起来一直是主要的,其中包含 1000 个提交。

我的建议是: 在合并期间始终检查目标分支(即,如果将 A 合并到 B,则检查 B)。

  • 如果您正在处理并行分支并希望定期从主分支引入更改,请检查您的并行分支。差异是有意义的——您将看到在主分支中对您的分支所做的更改。
  • 当您完成并行工作并希望将您的更改合并到主分支中时,请检查主分支并与您的并行分支合并。差异再次有意义 - 它会显示您对主分支的并行更改。

在我看来,日志的可读性很重要。

于 2017-08-31T05:11:29.010 回答
6

最好的方法实际上取决于其他人是否拥有您的代码的远程副本。如果分支仅在您的本地计算机上,您可以使用 rebase 命令以交互方式将功能分支中的提交应用到主分支:

git checkout master -b iphone-merge-branch
git rebase -i iphone

请注意,这会改变您新的iphone-merge-branch分支的提交历史记录,这可能会给其他人稍后试图将您的更改拉入他们的结帐时出现问题。相比之下,merge 命令将更改应用为新的提交,这在协作时更安全,因为它不会影响分支历史记录。有关使用 rebase 的一些有用提示,请参阅本文

如果您需要保持提交历史同步,最好执行合并。您可以使用git mergetool使用可视化差异工具以交互方式逐个修复冲突(可以在此处找到有关此的教程):

git checkout master -b iphone-merge-branch
git merge iphone
git mergetool -t kdiff3

第三种选择,如果你想绝对控制这个过程,那就是使用git cherry-pick。您可以使用 gitk(或您最喜欢的历史查看器)查看 iphone 分支中的提交哈希,记下它们,然后将它们单独挑选到合并分支中 - 随时修复冲突。可以在此处找到有关此过程的说明。此过程将是最慢的,但如果其他方法不起作用,则可能是最好的后备选项:

gitk iphone
<note down the 35 commit SHA hashes in this branch>
git checkout master -b iphone-merge-branch
git cherry-pick b50788b
git cherry-pick g614590
...
于 2010-02-28T14:42:23.467 回答
2

你说:

与master相比,iphone分支非常古老

你真的想把它们合并成一个新的分支吗?

master 和 iphone 分支的目的现在会非常不同(因为 iphone 已经很老了)。将iphone与master的祖先合并的新分支会更好吗?想想看。

我强烈建议您阅读Fun with merges and purpose of branches

阅读完那篇文章后,如果您仍然想合并 iphone 和 master,那么 @seanhodges 会解释如何很好地处理冲突。

于 2010-02-28T15:02:27.123 回答
1

您可能希望在实验之前对您的工作进行本地备份,以便在您停止了解发生的情况时重新启动。

如果您想保留它以供参考,请为您的工作创建一个备份分支,以免在变基期间丢失它。

git checkout iphone -b iphone_backup

从 master 创建一个新分支

git checkout master -b iphone31

然后在它之上重新定位。

git rebase -i --onto iphone31 iphone

上面的 Sean 关于 rebase 一次应用一个提交是正确的。但是不要担心你重新定位到的分支上的现有提交——它们不会被改变。Rebase 将新的(适应的)提交放在它们之上。一次完成一个提交时,您可以更好地控制冲突。但重要的是,您必须将您的工作重新建立在 master 之上,而不是反过来,因此 master 的历史不会改变。

或者,您只能做

git rebase -i master iphone

如果您不关心备份 iphone 分支。查看 rebase 文档以获取详细信息和替代方法http://git-scm.com/docs/git-rebase

于 2014-05-09T07:10:40.200 回答