'git merge'和'git rebase'有什么区别?


499

git merge和之间有什么区别git rebase


14
由于我的答案已删除,请访问此链接以获取此问题的正确答案:git-scm.com/book/en/Git-Branching-Rebasing#The-Basic-Rebase
HiB

6
顺便说一句,我将添加此站点。您需要了解的有关git的所有信息都可以通过玩游戏来学习:pcottle.github.io/learnGitBranching
Rollyng 2013年

首先阅读以下内容:git-scm.com/book/en/v2/…然后:git-scm.com/book/en/v2/Git-Branching-Rebasing您将真正理解。
Liber 2014年

Answers:


867

假设原来有3个提交,ABC

美国广播公司

然后,开发人员Dan创建了commit D,而开发人员Ed创建了commit E

美国广播公司

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

合并

ABCDEM

两个提交DE仍在此处,但是我们创建了合并提交M,该合并提交继承了D和的更改E。但是,这会产生菱形,许多人感到非常困惑。

REBASE

ABCDER

我们创建commit R,其实际文件内容与M上面的merge commit相同。但是,我们摆脱了commit E,就像它不存在一样(用点表示-消失线表示)。由于这种淘汰,E因此对于开发人员Ed来说应该是本地的,并且永远不要被推送到任何其他存储库。变基的优点是避免了钻石形状,并且历史沿直线一直很好-大多数开发人员都喜欢这样做!


53
漂亮的插图。但是,我不完全同意处理基准的积极态度。在合并和变基中,都可能发生需要手动解决的冲突。与往常一样,当程序员参与进来时,错误和错误的可能性是不可忽略的。如果发生合并错误,则整个团队或社区都可以查看合并并验证是否在此处引入了错误。rebase的历史记录保留在1个开发人员的repo中,即使在那里,reflog中的生存期也很有限。看起来更好,但是没有其他人可以很容易地看到出了什么问题。
Uwe Geuder 2013年

>“但是,这会产生钻石形状,许多人感到非常困惑。” 嗯...你能详细说明吗?
Greg Maletic

@GregMaletic:钻石形状是非线性历史。我不了解您,但总体上我不喜欢非线性事物。就是说,如果您真的喜欢钻石,欢迎您使用合并钻石-没有人强迫您。
mvp 2014年

1
尽管此答案非常有帮助,但最好是添加带有简单foo.txt文件的实际git命令以在本地重现它。就像上一个用户所说的,谁在进行基准变迁并不清楚。

1
@pferrel:我认为您没有正确理解它。git merge不交错提交(但通过查看可能会出现git log)。取而代之的是git merge保持Dan和Ed的两个发展历史不变,这是一次从每个角度来看的结果。git rebase使得Dan首先从事此工作,而Ed跟随他。在这两种情况下(合并和变基),实际的结果文件树是绝对相同的。
mvp

158

我真的很喜欢我讨厌git的10件事摘录(它在第二个示例中简要介绍了rebase):

3.糟糕的文档

手册页是一个全能的“ f *** you” 1。它们从计算机科学家而不是用户的角度描述命令。例子:

git-push – Update remote refs along with associated objects

这是人类的描述:

git-push – Upload changes from your local repository into a remote repository

更新,另一个示例:(感谢cgd)

git-rebase – Forward-port local commits to the updated upstream head

翻译:

git-rebase – Sequentially regenerate a series of commits so they can be 
             applied directly to the head node

然后我们有

git-merge - Join two or more development histories together

这是一个很好的描述。


1.未经审查的


问题不在于语言,也不在于用户是否是计算机科学家。尽管以另一种方式重新措词有助于弄清目的(即发生了什么),但未能说明手段(发生的方式)。Git需要了解手段才能理解目的。正是在理解使Git如此困难的方法。作为一种用于简化或减少任务所需工作量的工具,Git严重失败。可悲的是,太多的开发人员未能意识到这一点。
ATL_DEV

128

我个人认为标准图表绘制技术不是很有用-箭头似乎总是为我指出错误的方向。(它们通常指向每个提交的“父”,最终导致时间倒退,这很奇怪)。

用语言解释:

  • 当你衍合你的分支到自己的分公司,你让Git使它看起来好像你清晰地检查了自己的分支,然后做所有的工作从那里开始。这样一来,干净的,概念上很简单的变更包就可以供他人查看。当其分支上有新更改时,您可以再次重复此过程,并且最终总会在其分支的“尖端”获得一组干净的更改。
  • 当您其分支合并到分支中时,此时将两个分支历史联系在一起。如果以后再进行更多更改,将开始创建历史的交错线程:它们的某些更改,我的某些更改,它们的某些更改。有些人觉得这很杂乱或不受欢迎。

由于我不明白的原因,用于Git的GUI工具从没有做出太多的努力来更清晰地呈现合并历史,从而抽象出各个合并。因此,如果您需要“干净的历史记录”,则需要使用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分支的其余部分。

免责声明:我是另一个答案中提到的“我讨厌Git的十件事”的作者


42

尽管公认的最受好评的答案是不错的,但我发现仅用文字来解释区别也很有用:

合并

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

重新设定

  • “将主分支(无论其名称如何)的更改都交给我的功能分支。假装我的特征工作是在以后开始的,实际上是在主分支的当前状态下进行的。”
  • “重写我的更改历史以反映这一点。” (需要强行推送它们,因为通常版本控制只不过是篡改给定的历史记录而已)
  • “就像—如果我所进行的更改与我的工作几乎没有关系—如果我逐个查看我的提交(您可能还会想到'补丁'),历史实际上将不会发生太大变化。”

摘要:可能的话,变基几乎总是更好。使重新集成到主分支变得更加容易。

因为?feature您的特色作品可以相对于主分支显示为一个大的“补丁文件”(又称为diff),而不必“解释”多个父级:至少两个,来自一次合并,但如果有的话,可能更多是几次合并。与合并不同,多个基准不会累加。(另一大优点)


18

Git rebase更接近合并。rebase的区别是:

  • 本地提交会暂时从分支中删除。
  • 运行git pull
  • 再次插入所有本地提交。

因此,这意味着在所有远程提交之后,所有本地提交都将移至末尾。如果您有合并冲突,则也必须解决它。


7

为了容易理解可以看到我的身影。

Rebase将更改提交哈希,因此,如果您想避免很多冲突,只需在该分支完成/完成为稳定时使用rebase。

在此处输入图片说明


0

我在git rebase vs merge上找到了一篇非常有趣的文章,想在这里分享

  • 如果要查看与发生的历史完全相同的历史,则应使用merge。合并会保留历史记录,而重新设置基础会重写历史记录。
  • 合并为您的历史记录添加了新的提交
  • 重定基最好简化复杂的历史记录,您可以通过交互式重定基来更改提交历史记录。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.