git rebase如何/为什么避免合并提交?

时间:2018-09-11 16:32:51

标签: git git-merge git-rebase

git rebase命令在幕后使用合并,所以我想知道git rebase如何/为什么避免合并提交,而git merge通常会在分支之间存在差异时创建新的提交。

5 个答案:

答案 0 :(得分:4)

rebase不是像合并分支那样的合并。相反,rebase是一系列git cherry-pick。樱桃镐选择一个提交并将其复制,就好像它是在其他某个提交之上编写的一样。就像您将提交的差异应用到其他地方一样。

考虑这样的存储库。 A,B,C等代表提交ID。

A - B - C - D [master]
         \
          E - F - G [feature]

如果master需要E,也许这是一个关键的错误修复程序,但不是feature的其余部分,它可以git cherry-pick E。 Git将以C为原点在D和E之间进行合并,从而得到E1。这就是为什么在重新设置基准时可能会发生合并冲突的原因。但是与普通合并不同,E1只有一个父级。

A - B - C - D - E1 [master]
         \
          E - F - G [feature]

重新设置就像是否对您的所有提交都执行了。并且移动master而不是放在feature上方。这是git rebase master的逐步说明。

再次考虑您的存储库。

A - B - C - D [master]
         \
          E - F - G [feature]

首先,如前所述,它在E上执行cherry-pick,并将其放在master的顶部。但是,它没有移动master,而是创建了一个没有标签的新分支。

              E1
             /
A - B - C - D [master]
         \
          E - F - G [feature]

然后将其插入F并置于E1之上。

              E1 - F1
             /
A - B - C - D [master]
         \
          E - F - G [feature]

然后樱桃选择G并将其放在F1上作为G1。

              E1 - F1 - G1
             /
A - B - C - D [master]
         \
          E - F - G [feature]

完成摘樱桃后,它将feature分支标签移至G1。

              E1 - F1 - G1 [feature]
             /
A - B - C - D [master]
         \
          E - F - G

是的,旧的提交仍然存在。如果没有引用它们的信息(例如标签或其他分支),则将在几周内将其回收。同时,您仍然可以使用git reflog访问它们。有关更多信息,请参见Maintenance and Data Recovery chapter of Pro Git

答案 1 :(得分:3)

我喜欢在Git中将合并的概念分为两​​部分:

  • 动词要合并表示要合并更改。许多命令都使用此命令。

  • 名词 a merge ,源自形容词形式 merge commit ,其中形容词“ merge”修饰名词“ commit”,表示 a与两个或更多父母在一起。只有git merge会生成此类提交(嗯,git merge或运行git merge的事物,包括git pull)。

这意味着git rebase使用动词形式 merge 操作,该操作通常通过重复的git cherry-pick发生。 1 Git通过执行以提交的parent 2 作为合并基础的合并,将该提交本身作为“其他”提交,以及当前的HEAD提交发挥其正常作用的方式,樱桃提交。 zzxyz's comment因此是正确的:每次执行摘樱桃操作都可能导致合并冲突,这是人类必须解决的。

每个Cherry-pick操作的结果是每个提交的副本,如Schwern noted in his answer。复制感兴趣的提交后,Git移动分支名称,使其指向最终复制的提交-新分支的尖端在提交图中的新位置。由于ORIG_HEAD(在移动分支名称之前设置了rebase)以及由于各种reflog(对于HEAD本身和/或分支,原始提交至少保留了一段时间) git rebase刚刚更改的名称。

最后需要注意的是上一段中的利益承诺。当rebase对将要提交的提交进行布局时,默认情况下故意放弃合并。在此过程的任一侧都有论点(即“保持合并”与“丢弃合并”),但这就是Git今天要做的。因此,在重新定基后,即使复制提交的过程涉及 merge 动词,副本也不会包含任何合并提交。


1 交互式git rebase -i或与git rebase-m一起运行的-s strategy实际上使用git cherry-pick。没有-m-s的非交互式rebase使用git format-patchgit am -3,这在技术上在几个方面都较差,但向后兼容古老的Git 1.5。

2 这就是很难进行挑选的原因:合并-合并提交,根据定义是具有两个或多个父级的提交-没有 a 父级,它有两个或更多父级。 git cherry-pick命令可以让您随意选择它,但要这样做,您必须指定哪个父命令,该命令应将其作为“动词合并”过程中的合并基础。

答案 2 :(得分:2)

您可以这样考虑:git rebase从分支中提取新提交并将其放在一边,然后倒回分支以匹配更新的原始提交,最后在结果之上添加您的提交; git merge仅使用分支之间的差异创建一个新的(合并)提交。请注意,在重新部署原始分支上不存在的所有提交时,它们的哈希值都会改变,因此您将不得不强行推入新分支(如果有多个人在同一分支上进行协作,这并不总是那么容易科)。但是,通过重定基础,您可以避免合并提交并保持更清晰的历史记录。

答案 3 :(得分:1)

您是正确的,即基本是在后台进行合并。实际上,您可以使用merge命令创建自己的rebase命令,而您所需要做的就是随后修改一些提交元数据。

让我们说这是您的历史记录

A - B - C (master)
 \
  D - E (dev)

如果您希望将dev改编为master,可以先合并D然后合并E,

A - B - C - D'- E'
 \         /   /
  + - - - D - E

然后您修改提交以删除D和E中的链接,

A - B - C - D"- E" (master)
 \
  D - E (dev)

因此,您可以看到git rebase仅仅是合并之上的一些简单的历史操作(仅更改元数据)。您看不到合并提交的原因仅仅是一个元数据问题-每个提交都有指向其父提交的指针,如果删除除其中一个指针以外的所有指针,则会删除该提交与那些父提交的连接,从而将合并提交变成非合并提交。

也可以用这种方法完成更复杂的变基。请注意,这并不是重新设置实际的工作方式,它只是您可以手动重新设置提交内容而无需使用rebase或cherry-pick命令的一种方式。重要的是,合并提交无非就是两个或多个父级的提交,您可以通过修改元数据来更改任何提交的父级。

答案 4 :(得分:1)

很多人回答了如何 rebase避免合并提交,但是我认为为什么有点有趣。您可以用它做很多事情,所以这只是我对最重要的观点。 (其他具有不同工作流程的人自然会不同意:)

在集中式存储库系统(例如svnperforce)中,提交直接完成到服务器。因此,如果Bob和Jim都在主分支的本地副本上工作,那么他们都在主分支上工作。故事结局。在Bob和Jim做出承诺之后,我们可以按顺序在一个分支中看到它们。

git潜在极少缺点之一是,远程存储库实际上是 隐式分支。它的行为就像一个,它像在git日志中一样显示,等等。因此,如果Bob和Jim都对其本地副本进行了更改,则要推送的最后一个必须拉出...并从远程进行合并。这样看来,鲍勃和吉姆都在各自的分支机构工作。有时,这种行为是完美的。有时这是不可取的。

git pull --rebase / git fetch;git rebase是告诉git的方式:“我知道认为这是一个分支,但我不知道。我只想像正常人一样,在最后一个承诺的基础上做我的承诺,并拥有我认为的准确分支历史。”我个人不太关心干净