'git merge'And 'git rebase'

git mergegit rebase有什么区别?

246197 次浏览

假设最初有3个提交,# EYZ0,BC:

< img src = " https://i.stack.imgur.com/lJRq7.png " alt = " a - b - c”>

然后开发人员Dan创建了提交D,开发人员Ed创建了提交E:

< img src = " https://i.stack.imgur.com/pK7Zb.png " alt = " A-B-C-D-E”>

显然,这种冲突应该以某种方式解决。对此,有两种方法:

# EYZ0:

< img src = " https://i.stack.imgur.com/9Ul5w.png " alt = " A-B-C-D-E-M”>

两个提交DE仍然在这里,但是我们创建了合并提交M,它继承了来自DE的更改。然而,这创建了钻石形状,许多人觉得非常困惑。

# EYZ0:

< img src = " https://i.stack.imgur.com/TvXuJ.png " alt = " A-B-C-D-E-R”>

我们创建提交R,其实际文件内容与上面的合并提交M相同。但是,我们摆脱了commit E,就像它从来没有存在过一样(用圆点-消失线表示)。由于这种删除,E对于开发人员Ed来说应该是本地的,并且不应该被推到任何其他存储库。rebase的优点是避免了钻石形状,历史保持良好的直线-大多数开发人员都喜欢这样!

就我个人而言,我不认为标准的图表技术非常有用——箭头似乎总是指向错误的方向。(它们通常指向每次提交的“父”,这最终会在时间上倒退,这很奇怪)。

用语言解释一下:

  • 当你变基你的分支到他们的分支时,你告诉Git让它看起来好像你干净地签出了他们的分支,然后从那里开始你的所有工作。这样就形成了一个清晰的、概念上简单的更改包,供人检查。当他们的分支上有新的更改时,您可以再次重复这个过程,并且您总是会在他们的分支的“尖端”上得到一组干净的更改。
  • 当您合并他们的分支到您的分支时,您将在此时将两个分支历史绑定在一起。如果你以后再这样做,做更多的改变,你就开始创造一个交错的历史线索:一些是他们的改变,一些是我的改变,一些是他们的改变。有些人觉得这样很乱或不受欢迎。

出于我不理解的原因,Git的GUI工具从来没有更清晰地呈现合并历史,抽象出单个合并。所以如果你想要一个“干净的历史”,你需要使用rebase。

我似乎记得读过一些程序员的博客文章,他们只有使用rebase,其他人从来没有使用rebase。

例子

我将用一个简单的例子来解释这一点。假设项目中的其他人正在处理用户界面,而你正在编写文档。如果没有rebase,你的历史可能看起来像这样:

Write tutorial
Merge remote-tracking branch 'origin/master' into fixdocs
Bigger buttons
Drop down list
Extend README
Merge remote-tracking branch 'origin/master' into fixdocs
Make window larger
Fix a mistake in howto.md

也就是说,合并和UI在文档提交的中间提交。

如果你将你的代码重新基于master而不是合并它,它会像这样:

Write tutorial
Extend README
Fix a mistake in howto.md
Bigger buttons
Drop down list
Make window larger

您的所有提交都在顶部(最新的),后面是master分支的其余部分。

(# EYZ0)

虽然被广泛接受且获得最多好评的答案很棒,但我发现仅用语言来解释两者的差异是有用的:

合并

  • “好吧,我们的存储库有两种不同的开发状态。我们把它们合并在一起。两个父母,一个孩子。”

变基

  • “把主分支(不管它叫什么)的变化给我的功能分支。要做到这一点,就假装我的特性工作开始得较晚,实际上是在当前的主分支状态上。”
  • “重写我的改变的历史来反映这一点。“# EYZ0
  • “很可能——如果我所做的改变与我的工作无关——如果我逐条查看我的提交(你可能也会想到“补丁”),历史记录实际上不会有太大变化。”

简介:如果可能,rebase总是更好。使重新集成到主分支更容易。

因为?➝你的功能工作可以呈现为一个大的“补丁文件”(又名diff)相对于主分支,不需要“解释”多个父:至少两个,来自一个合并,但可能更多,如果有几个合并。与merge不同,多个rebase不能叠加(另一个大优点)

Git rebase更接近于merge。rebase的区别是:

  • 本地提交被临时地从分支中移除。
  • 运行git pull
  • 再次插入所有本地提交。

这意味着所有本地提交都被移动到最后,在所有远程提交之后。如果您遇到合并冲突,也必须解决它。

为了便于理解可以看到我的身影。

Rebase将改变提交哈希,因此如果你想避免大量冲突,只需在分支完成/完成时使用Rebase作为稳定。

enter image description here

我发现了一篇关于git 重组vs合并的非常有趣的文章,想分享它在这里

  • 如果你想看到完全相同的历史,你 应该使用归并。
  • . Merge保存历史,而rebase重写历史
  • 合并会向历史记录中添加一个新的提交
  • 重基可以更好地简化复杂的历史记录,你可以通过交互的重基来改变提交历史。

mergerebase有什么区别?

阅读官方Git手册,它指出“在另一个基分支上重新应用提交”,而“merge将两个或多个开发历史连接在一起”。换句话说,merge和rebase之间的关键区别是merge保留了历史,而rebase重写了历史。

让我们用一个并列的例子来说明这些陈述!

 compare of merge vs rebase .

如上所述,merge操作通过创建一个新的合并提交(C7)将分支交织在一起,导致菱形非线性历史—本质上保留了历史发生时的历史。通过将此结果与rebase操作的结果进行比较,我们可以看到没有创建合并提交,相反,C5C6两次提交只是被倒带,并直接在C4上重新应用,保持历史记录的线性。

如果我们进一步仔细检查两次重新应用的提交,我们可以看到哈希值发生了变化,这表明rebase确实重写了历史。

值得注意的

每当你rebase一个分支,新的提交将总是生成,即使内容可能仍然相同!也就是说,如果没有其他指针(分支/标记)引用它们,任何以前的提交最终(垃圾收集后)将从历史记录中删除。

能力越大,责任越大

我们已经看到了rebase如何重写历史,而merge如何保存历史。但从更广泛的意义上来说,这意味着什么呢?这两种操作有哪些可能性和潜在的缺陷?

冲突性的变更

比方说,您在尝试集成这些更改时遇到了一些严重的冲突。在合并场景中,您只需要在C7提交中直接解决一次冲突。另一方面,使用rebase,您可能被迫在每次提交(C5C6)中解决类似的冲突,因为它们被重新应用。

发布分支

另一个与改基相关的潜在问题发生在您要改基的分支已经远程发布,而其他人的工作已经基于该分支进行了。然后,重新基于的分支可能会给所有相关方带来严重的困惑和头痛,因为Git会告诉您,您的分支同时处于领先和落后状态。如果发生这种情况,使用rebase标志拉出远程更改(git pull——rebase)通常可以解决问题。

此外,无论何时您要基于一个已经发布的分支进行重基,无论其他人是否基于它进行工作,您仍然需要强制推送它以将您的更新发送到远程服务器—完全覆盖现有的远程引用。

数据丢失(对您有利)

最后,由于rebase重写历史,而merge保留历史,因此在rebase时实际上可能会丢失数据。当重新应用新的提交时,旧的提交将被删除(最终,在垃圾收集之后)。同样的特性实际上是rebase如此强大的原因——它允许您在公开之前整理您的开发历史!

结论

虽然从潜在的数据丢失角度来看,merge是安全的,而且使用起来更直接。这里有一些提示,可以帮助您避免与rebase相关的最常见问题。

  • 不要对远程发布的分支进行重基…
  • 除非你知道你是唯一一个在做这件事的人(而且你觉得推这件事很安全)
  • 在重基之前,从将要重基的分支的顶端创建一个备份分支,因为它将允许您轻松地比较结果(一旦完成),并在必要时跳回到重基前的状态。

以上节选自这篇关于主题的完整文章:Git Merge和Rebase之间的区别-以及为什么你应该关心

假设你已经在你的特征分支中提交了3次,当你想把你的特征分支的变化发送到主分支时。 你有两个选项

  1. git merge:在这种情况下,主分支将只接收1个提交(结合3个提交)
  2. git rebase:在这种情况下,主分支将接收3次提交