建议何时使用Git rebase与Git merge?

成功重新创建数据库后,是否仍需要合并?


当前回答

我刚刚用自己的话为我的团队创建了一个FAQ,以回答这个问题。让我分享一下:

什么是合并?

提交,将不同分支的所有更改合并到当前。

什么是重基?

将当前分支的所有提交重新提交到不同的基本提交上。

合并和再基础之间的主要区别是什么?

merge只执行一次新提交。rebase通常执行多次(当前分支中的提交次数)。merge生成一个新生成的提交(所谓的合并提交)。rebase仅移动现有提交。

在哪些情况下我们应该使用合并?

如果要将分支分支的更改添加回基本分支,请使用merge。

通常,您可以通过单击Pull/Merge请求上的“合并”按钮来完成此操作,例如在GitHub上。

在哪些情况下我们应该使用重基?

每当您想将基本分支的更改添加回分支时,请使用rebase。

通常,只要主分支发生变化,就可以在功能分支中执行此操作。

为什么不使用合并将基本分支中的更改合并到要素分支中?

git历史记录将包含许多不必要的合并提交。如果在一个功能分支中需要多个合并,那么该功能分支甚至可能包含比实际提交更多的合并提交!这就产生了一个循环,它破坏了Git所设计的心理模型,这在Git历史的任何可视化中都会带来麻烦。想象有一条河(例如“尼罗河”)。水流向一个方向(Git历史上的时间方向)。有时,想象那条河有一条支流,假设这些支流中的大部分都汇回了这条河。这就是河流的自然流动的样子。这是有道理的。但想象一下,那条河有一条小支流。然后,由于某种原因,河流汇入分支,分支从那里继续。从技术上讲,这条河现在已经消失了,它现在在支流中。但是,不知怎的,这条支流神奇地汇回了河里。你问哪条河?我不知道。这条河现在应该在支流中,但不知何故它仍然存在,我可以将支流合并回河流中。所以,河在河中。有点说不过去。这正是将基本分支合并到要素分支时发生的情况,然后在要素分支完成后,再次将其合并回基本分支。心智模型被打破了。因此,你最终得到了一个没有太大帮助的分支可视化。

使用merge时的Git历史示例:

请注意,许多提交都是从Merge分支“main”开始的。。。。如果你重新创建数据库,它们甚至都不存在(在那里,你只会有拉请求合并提交)。还有许多视觉分支合并循环(主到要素到主)。

使用rebase时的Git历史示例:

Git历史更加清晰,合并提交更少,没有任何杂乱的可视化分支合并循环。

rebase有什么缺点/缺陷吗?

Yes:

因为重基会移动提交(从技术上讲是重新执行),所以所有移动的提交的提交日期将是重基的时间,而git历史记录看起来可能会丢失初始提交时间。因此,如果出于某种原因,所有工具都需要提交的确切日期,那么合并是更好的选择。但通常情况下,干净的git历史要比准确的提交日期有用得多。如果需要,authordate字段将继续保存原始提交日期。如果重基分支有多个更改同一行的提交,并且该行也在基分支中更改,则可能需要多次解决该行的合并冲突,这在合并时是不需要的。因此,平均而言,需要解决的合并冲突更多。

使用rebase时减少合并冲突的提示:

经常退款。我通常建议每天至少做一次。尽量将同一行中的更改压缩为一次提交。

其他回答

Gitrebase用于使历史中的分支路径更清晰,存储库结构更线性。

它还用于将您创建的分支保持为私有,因为在重新创建基础并将更改推送到服务器之后,如果您删除了分支,则不会有您曾处理过的分支的证据。因此,您的分支现在是您的本地关注点。

在进行重新基化之后,我们还消除了一个额外的提交,这是我们用来查看是否进行正常合并的。

是的,在成功的rebase之后仍然需要进行合并,因为rebase命令只是将您的工作放在您在rebase过程中提到的分支之上,比如master,并将分支的第一次提交作为master分支的直接后代。这意味着我们现在可以进行快速合并,将更改从该分支带到主分支。

虽然合并绝对是集成更改的最简单和最常见的方式,但它不是唯一的方式:Rebase是一种替代的集成方式。

更好地理解合并

当Git执行合并时,它会查找三个提交:

(1) 共同祖先提交。如果您遵循一个项目中两个分支的历史,它们总是至少有一个共同的提交:此时,两个分支都具有相同的内容,然后演变不同。(2) +(3)每个分支的端点。集成的目标是合并两个分支的当前状态。因此,它们各自的最新修订值得特别关注。将这三个提交结合起来,将实现我们的目标。

快进或合并提交

在非常简单的情况下,两个分支中的一个分支自分支发生以来没有任何新的提交——其最新的提交仍然是共同的祖先。

在这种情况下,执行集成非常简单:Git只需将其他分支的所有提交添加到共同祖先提交之上即可。在Git中,这种最简单的集成形式被称为“快进”合并。然后,两个分支共享完全相同的历史。

然而,在很多情况下,这两个分支都各自向前移动。

为了进行集成,Git必须创建一个包含它们之间差异的新提交,即合并提交。

人工提交和合并提交

通常,一个承诺是由一个人精心创造的。这是一个有意义的单元,它只包装相关的更改并用注释注释它们。

合并提交有点不同:它不是由开发人员创建的,而是由Git自动创建的。而不是包装一组相关的更改,其目的是连接两个分支,就像一个结一样。如果以后想了解合并操作,需要查看两个分支的历史记录和相应的提交图。

与Rebase集成

有些人更喜欢不进行这种自动合并提交。相反,他们希望项目的历史看起来像是沿着一条直线发展的。没有迹象表明它在某一时刻被拆分为多个分支。

让我们一步一步地完成一个rebase操作。场景与前面的示例相同:我们希望将分支B的更改集成到分支A中,但现在使用rebase。

我们将分三步完成

git rebase branch-A//将历史与branch-A同步git checkout branch-A//将当前分支更改为branch-Agitmergebranch-B//合并/从branch-B到branch-A进行更改

首先,Git将“撤消”分支A上的所有提交,这些提交发生在行开始分支之后(在共同祖先提交之后)。然而,当然,它不会丢弃它们:相反,您可以将这些提交视为“暂时保存”。

接下来,它应用我们要集成的分支B的提交。此时,两个分支看起来完全相同。

在最后一步中,分支A上的新提交现在被重新应用,但在分支B的集成提交之上的一个新位置上(它们是基于重新的)。

结果似乎是直线发展。与包含所有合并更改的合并提交不同,保留了原始提交结构。

最后,您得到了一个干净的分支分支a,没有不需要的和自动生成的提交。

注:摘自git tower的精彩帖子。在同一篇文章中,rebase的缺点也是一个很好的解读。

信息图形总是有用的:)

合并:将一个分支重叠到另一个分支上

Assume the following history exists and the current branch is "master":

                     A---B---C topic
                    /
               D---E---F---G master

       Then "git merge topic" will replay the changes made on the topic branch since it
       diverged from master (i.e., E) until its current commit (C) on top of master, and
       record the result in a new commit along with the names of the two parent commits
       and a log message from the user describing the changes.

                     A---B---C topic
                    /         \
               D---E---F---G---H master

Rebase:将一个分支的更改移动到另一个分支末端

Assume the following history exists and the current branch is "topic":

                     A---B---C topic
                    /
               D---E---F---G master

       From this point, the result of either of the following commands:

           git rebase master
           git rebase master topic

       would be:

                             A'--B'--C' topic
                            /
               D---E---F---G master

       NOTE: The latter form is just a short-hand of git checkout topic followed by git
       rebase master. When rebase exits topic will remain the checked-out branch.

因此,我们基本上可以得出结论,合并是一个安全的选项,它可以保存存储库的整个历史,而重基化通过将特性分支移动到main的顶端来创建线性历史。

Credit:帮助页面gitmerge--help和gitrebase--help

这很简单。使用rebase时,您可以使用另一个分支作为工作的新基础。

例如,如果您有一个分支主节点,您可以创建一个分支来实现一个新特性,并将其命名为酷特性,当然,主分支是新特性的基础。

现在,在某一点上,您希望添加在主分支中实现的新特性。您可以切换到master并合并酷功能分支:

$ git checkout master
$ git merge cool-feature

但这样就添加了一个新的虚拟提交。如果你想避免意大利面条的历史,你可以重新设定基准:

$ git checkout cool-feature
$ git rebase master

然后将其合并到master中:

$ git checkout master
$ git merge cool-feature

这一次,由于主题分支具有与master相同的提交,再加上具有新特性的提交,因此合并将是一个快速前进。

一些与Gerrit用于审查和交付集成的大规模开发相关的实际示例:

当我将我的功能分支提升到一个新的远程主机时,我就合并了。这提供了最小的提升工作,并且很容易跟踪功能开发的历史,例如gitk。

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

我在准备交付提交时合并。

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

无论什么原因,当我的交付提交未能集成时,我都会重新启动,我需要将其更新为新的远程主机。

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master