git:合并两个分支:什么方向?

我们有以下情况:

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

在最后的工作和iPhone之间,提交了32个提交。 在最后的工作和主人之间,做了很多提交。

我现在想要的是一个新的分支,我有iphone和当前的主人合并在一起。 在稍后的时间,这应该被合并到主。

首先,我打算做:

 git checkout iphone -b iphone31 git merge master 

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

 git checkout master -b iphone31 git merge iphone 

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

我已经尝试了两个,正如我所料,我得到了很多的冲突,因为iPhone相比,硕士真的是老。 现在我想知道最简单的方法来合并它们。

也许甚至开始与主和合并每一个单一的提交到它会更容易? 像这样做:

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

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

 git checkout master git merge iphone31 

关于替代scheme

 git checkout iphone -b iphone31 git merge master 

 git checkout master -b iphone31 git merge iphone 

他们会有同样的安逸或困难,就像争论一杯是半满还是半空。

版本树感知

我们如何看待版本树在某种程度上就是我们的任意感知。 假设我们有如下的版本树:

  A----------+ | | | | \_/ \_/ BX | | | | \_/ \_/ CY | | \_/ D | | \_/ E 

假设我们希望根据从C到E的变化从Y检出一个新版本Z,但是不包括从A到C的变化。

“哦,那将是困难的,因为没有共同的起点。” 那么不是真的。 如果我们只是在这样的graphics布局放置对象有点不同

  / C+---------+ | \ | | | \_/ | DB | / \ | | \_/ | EA | | \_/ X | | \_/ Y 

现在事情开始看起来很有希望了。 请注意,这里我没有改变任何关系,箭头和前面的图片一样,版本A仍然是共同的基础。 只有布局被改变。

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

  C'---------+ | | | | \_/ \_/ DB' | | | | \_/ \_/ EA | | \_/ X | | \_/ Y 

其中的任务只是正常地合并版本E.

所以你可以合并任何你想要的东西,影响简单或困难的唯一的事情就是在你select的起点或者共同基地和你合并的地方之间所做的改变的总和。 您并不局限于使用您的版本控制工具build议的自然起点。

这可能不是一些版本控制系统/工具简单,但如果一切都失败了,没有任何东西阻止你通过检查版本C并将文件保存为file1,检出版本E并将文件保存为file2 ,检出版本Y并将文件保存为file3,然后运行kdiff3 -o merge_result file1 file2 file3

回答

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

我的build议是,因为在last-working和iphone之间有32个提交,所以你可以开始分支master,然后在前16个提交合并。 如果这太麻烦了,回复并尝试合并8个第一次提交。 等等。 在最糟糕的情况下,您最终将逐个合并32个提交中的每一个,但是这可能比在一个合并操作中处理所有积累的冲突更容易(在这种情况下,您正在使用一个非常分散的代码库) 。

提示:

在纸上画一个版本树,用箭头记下你想合并的东西。 如果分几个步骤来分解这个过程,就把它们分离出来。 这将使你更清楚地了解你想达到的目标,到目前为止所做的和剩下的。

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

最好的方法真的取决于其他人是否有你的代码的远程副本。 如果分支仅在本地计算机上,则可以使用rebase命令以交互方式将特征分支中的提交应用于主设备:

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

请注意,这会改变新的iphone-merge-branch分支的提交历史logging,这可能会导致其他任何尝试将更改拖放到他们的结帐中的问题。 相比之下,merge命令将更改作为新提交应用,因此在协作时更安全,因为它不影响分支历史logging。 有关使用rebase的一些有用技巧,请参阅这篇文章 。

如果您需要保持提交历史logging同步,则最好执行合并。 你可以使用git mergetool交互式地使用可视化差异工具来修复冲突(关于这个的教程可以在这里find ):

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

第三个选项,如果你想绝对控制过程,将使用git cherry-pick 。 你可以使用gitk(或者你最喜欢的历史查看器)来查看iphone分支中的提交散列,记下它们,樱桃把它们分别放入合并分支中 – 修复冲突。 这个过程的解释可以在这里find 。 这个过程将是最慢的,但如果其他方法不能解决,可能是最好的回退选项:

 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 ... 

你说:

相比掌握iphone分支是非常老的

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

主和iPhone分支的目的现在已经非常不同了(因为iPhone是非常古老的)。 合并iPhone与主人祖先的新分支会更好? 想想看。

我强烈build议您阅读合并和分支目的的乐趣 。

如果你仍然觉得你想要合并iPhone和主人阅读这篇文章,然后@seanhodges解释如何处理好的冲突。

在试验之前,您可能需要对您的工作进行本地备份,以便您可以重新启动,如果您停止了解发生的事情。

做一个你的工作的备份分支,所以不要失去它在rebase,如果你想保留作为参考。

 git checkout iphone -b iphone_backup 

从主创build一个新的分支

 git checkout master -b iphone31 

然后重新绑定它。

 git rebase -i --onto iphone31 iphone 

上面的肖恩是正确的一次申请一个提交的基准。 但不要担心你重新分配的分支上的现有提交 – 他们不会被改变。 Rebase把新的(适应)提交放在他们之上。 一次完成一个提交,你可以更好地控制冲突。 重要的是,你必须在主人之上重新分配你的工作,而不是相反,所以主人的历史不会改变。

或者,你只能做

 git rebase -i master iphone 

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

它们是有区别的。


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


人们常说合并的方向并不重要,但这是错误的。 虽然不pipe合并方向如何,结果内容都是相同的,但是有几个方面是不同的:

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

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

  • 你从MASTER处分出1000个提交,并将其命名为DEVELOP(或者在跟踪分支场景中,你已经有相当一段时间没有提取)。
  • 您将一个提交添加到DEVELOP。 你知道这个改变没有冲突。
  • 你想把你的改变推到MASTER。
  • 您错误地将MASTER合并 DEVELOP(即在合并过程中检出了DEVELOP)。 然后,您将DEVELOP作为新的MASTER。
  • 在生成的合并提交中的差异将显示在MASTER中发生的所有1000次提交,因为DEVELOP是参考点。

这个数据不仅没有用处,而且很难读取正在发生的事情。 大多数可视化工具将使它看起来像您的发展线一直是主要的,有1000个提交带入它。

我的build议是这样的: 总是在合并期间检查出目标分支 (即,如果将A合并到B中,则检出B)。

  • 如果您正在并行分支上工作,并想定期从主分支引入更改,请检查并行分支。 差异将是有道理的 – 你会看到主分支与你的分支做的改变。
  • 当你完成并行工作并希望将更改合并到主分支时,签出主分支并与并行分支合并。 差异再次将是有道理的 – 它会显示你的平行变化与主分支。

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