mvp*_*mvp 838

假设原来有3个提交,A,B,C:

ABC

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

ABCDE

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

合并:

ABCDEM

提交DE仍然在这里,但我们创建合并提交M,继承来自D和的变化E.然而,这会产生钻石形状,许多人发现这种形状非常混乱.

REBASE:

ABCDER

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

  • 很好的插图.但是,我并不完全同意改变rebase的积极基调.在合并和rebase中,可能会发生需要手动解决的冲突.和程序员一样,有一个不可忽视的错误机会,即bug.如果发生合并错误,整个团队或社区可以看到合并并验证是否在那里引入了错误.rebase的历史保留在1个开发人员的回购中,甚至在reflog中只有有限的生命周期.它可能看起来更好,但没有其他人能够轻易地看到出了什么问题. (44认同)
  • > “然而,这会产生钻石形状,很多人觉得很困惑。” 嗯……能详细点吗? (4认同)
  • @GregMaletic:钻石的形状是非线性的历史。我不了解你,但我一般不喜欢非线性的东西。也就是说,如果您确实喜欢的话,欢迎您使用与钻石合并 - 没有人强迫您。 (4认同)
  • 虽然这个答案非常有用,但如果您使用简单的 foo.txt 文件添加实际的 git 命令以在本地重现它会更好。就像上一个用户所说的,谁在做 rebase 并不明显。 (3认同)
  • @Vortex:整个想法是给你解释这个概念。添加任何 git 命令只会掩盖真正发生的事情。 (2认同)

mvw*_*mvw 149

我真的很喜欢这个关于我讨厌git的10件事的摘录(在第二个例子中给出了一个关于rebase的简短解释):

3.糟糕的文档

手册页是一个万能的"f***you" 1.他们从计算机科学家的角度描述命令,而不是用户.例证:

git-push – Update remote refs along with associated objects
Run Code Online (Sandbox Code Playgroud)

这是人类的描述:

git-push – Upload changes from your local repository into a remote repository
Run Code Online (Sandbox Code Playgroud)

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

git-rebase – Forward-port local commits to the updated upstream head
Run Code Online (Sandbox Code Playgroud)

翻译:

git-rebase – Sequentially regenerate a series of commits so they can be 
             applied directly to the head node
Run Code Online (Sandbox Code Playgroud)

然后我们有

git-merge - Join two or more development histories together
Run Code Online (Sandbox Code Playgroud)

这是一个很好的描述.


1.未经审查的原件

  • 问题不在于语言,也不在于用户是否是计算机科学家。虽然以另一种方式重新措辞有助于澄清目的(即发生了什么),但它无法解释手段(它是如何发生的)。Git 需要理解手段才能理解目的。正是理解手段让 Git 变得如此困难。作为一种工具,一种用于简化或减少任务所需工作量的工具,Git 的失败非常严重。可悲的是,太多的开发人员没有意识到这一点。 (4认同)

Ste*_*ett 119

就个人而言,我没有发现标准图表技术非常有用 - 箭头似乎总是指向我错误的方式.(它们通常指向每个提交的"父",最终会在时间上倒退,这很奇怪).

用文字解释:

  • 当你衍合你的分支到自己的分公司,你让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
Run Code Online (Sandbox Code Playgroud)

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

如果您将代码重新设置为master而不是合并它,它将如下所示:

Write tutorial
Extend README
Fix a mistake in howto.md
Bigger buttons
Drop down list
Make window larger
Run Code Online (Sandbox Code Playgroud)

您的所有提交都位于顶部(最新),其次是master分支的其余部分.

(免责声明:我是另一个答案中提到的"我讨厌Git的10件事"的作者)

  • 提交图有指向父母而不是孩子的指针,_因为那是 git 存储的_。理解这一点——以及分支是指向其“尖端”处的单个提交的指针这一事实——对于理解 git 所做的许多事情非常有用。我也不知道你所说的“Git 的 GUI 工具从来没有付出太多努力来更清晰地呈现合并历史,抽象出各个合并”是什么意思。不过,您对变基和合并的实际描述很棒。 (2认同)

Fra*_*cke 39

虽然接受和最受欢迎的答案很棒,但我还发现尝试用单词解释差异很有用:

合并

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

变基

  • "将主分支(无论其名称)的更改提供给我的功能分支.通过假装我以后开始的功能工作来实现,实际上是在主分支的当前状态."
  • "重写我的更改历史以反映这一点." (需要强制推送它们,因为通常版本控制都是为了不篡改给定的历史记录)
  • "可能 - 如果我所做的改变与我的工作没什么关系 - 历史实际上不会有太大变化,如果我通过差异查看我的提交差异(你也可能会想到'补丁')."

摘要:如果可能,rebase几乎总是更好.使重新集成到主分支更容易.

因为?➝你的功能工作可以作为主要分支的一个大'补丁文件'(又名差异)呈现,而不必"解释"多个父母:至少两个,来自一个合并,但可能更多,如果有几次合并.与合并不同,多个rebase不会相加.(另一大优点)


Ale*_*ler 29

merge和 和有什么区别rebase

\n

阅读官方 Git 手册,它指出\xe2\x80\x9crebase 在另一个基本分支 \xe2\x80\x9d 之上重新应用提交,而\xe2\x80\x9cmerge 将两个或多个开发历史记录在一起 \xe2\x80\x9d。换句话说,合并和变基之间的主要区别在于,在merge保留发生的历史的同时,rebase重写它。

\n

让我们用一个并排的例子来将这些陈述置于上下文中!

\n

合并与变基的比较

\n

如上所示,该操作通过创建新的单个合并提交( C7merge )将分支交织在一起,从而产生菱形非线性历史 \xe2\x80\x94 ,本质上保留了发生时的历史。通过将此结果与操作的结果进行比较,我们发现没有创建合并提交,而是两个提交C5C6只是简单地倒回并直接在C4之上重新应用rebase,从而保持历史记录线性。

\n

如果我们进一步检查两个重新应用的提交,我们可以看到哈希值已经改变,这表明rebase真正重写了历史。

\n

值得注意

\n

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

\n

拥有权利的同时也被赋予了重大的责任

\n

我们\xe2\x80\x99已经看到rebase如何重写历史,而merge如何保留它。但这在更广泛的意义上意味着什么呢?这两种手术有哪些可能性和潜在缺点?

\n

冲突的变化

\n

让\xe2\x80\x99s 说,例如,你\xe2\x80\x99s 在尝试整合更改时遇到了一些令人讨厌的冲突。在合并场景中,您只需要直接在C7提交中解决一次冲突。另一方面,使用变基,您可能会被迫在重新应用每次提交( C5C6 )时解决类似的冲突。

\n

已发布分支

\n

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

\n

此外,每当您重新调整已发布的分支时,无论其他人是否基于该分支进行工作,您\xe2\x80\x99仍然需要强制推送它以将更新获取到远程服务器\xe2\x80\x94完全覆盖现有的远程引用。

\n

数据丢失(对您有利)

\n

最后,由于变基会重写历史记录,而合并会保留历史记录,因此变基时可能会实际丢失数据。当重新应用新的提交时,旧的提交将被删除(最终,垃圾收集后)。事实上,正是这个相同的特征使得 rebase 如此强大 \xe2\x80\x94 它允许您在公开之前整理您的开发历史!

\n

结论

\n

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

\n
    \n
  • 不要对远程发布的分支进行变基\xe2\x80\x99\xe2\x80\xa6
  • \n
  • \xe2\x80\xa6除非你知道你是唯一一个在这方面工作的人(并且感到安全的强​​力推动)
  • \n
  • 在变基之前,从您要变基的分支的顶端创建一个备份分支,因为它可以让您轻松比较结果(完成后),并在必要时跳回变基前的状态。
  • \n
\n

资料来源:以上摘录自关于该主题的完整帖子: Git Merge 和 Rebase \xe2\x80\x94 之间的差异以及为什么您应该关心

\n


Wil*_*nco 14

Git rebase更接近合并.rebase的区别在于:

  • 本地提交暂时从分支中删除.
  • 跑git拉
  • 再次插入所有本地提交.

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

  • “更接近合并”比什么? (2认同)

nag*_*547 6

我发现一篇关于 git rebase 与 merge的非常有趣的文章,想在这里分享

  • 如果你想看到与发生时完全相同的历史记录,你应该使用合并。合并保留历史,而变基重写历史。
  • 合并为您的历史记录添加了新的提交
  • 变基更好地简化了复杂的历史记录,您可以通过交互式变基来更改提交历史记录。


Abh*_*bhi 6

假设当您想要将功能分支更改发送到主分支时,您已经在功能分支中完成了 3 次提交。你有两个选择

  1. git merge:在这种情况下,主分支将仅收到 1 次提交(合并 3 次提交)
  2. git rebase:在这种情况下,主分支将收到 3 次提交


Nha*_*Cao 5

为了便于理解可以看到我的身材.

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

在此输入图像描述