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

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


当前回答

我什么时候使用git rebase?几乎不会,因为它改写了历史。gitmerge几乎总是首选,因为它尊重项目中实际发生的事情。

其他回答

TLDR:这取决于什么是最重要的——整洁的历史还是发展顺序的真实再现

如果一个整洁的历史是最重要的,那么你会先重新基址,然后合并你的更改,所以很清楚新代码是什么。如果你已经推送了你的分支,除非你能够处理后果,否则不要重新基址。

如果序列的真实表示是最重要的,那么您可以合并而不重定基础。

合并意味着:创建一个新的提交,将我的更改合并到目标中。注意:这个新的提交将有两个父级:一个是提交字符串中的最新提交,另一个是合并的另一个分支的最新的提交。

Rebase的意思是:使用我当前的一组提交作为提示,创建一系列新的提交。换言之,计算一下如果我从重新基准点开始进行更改,我的更改会是什么样子。因此,在重新基准之后,您可能需要重新测试您的更改,并且在重新基准期间,您可能会发生一些冲突。

既然如此,你为什么要重新基准?只是为了保持发展历史的清晰。假设您正在处理功能X,当您完成后,将您的更改合并到中。目标现在将有一个单独的提交,该提交内容类似于“添加的功能X”。现在,如果您重新创建基础,然后进行合并,那么目标开发历史将包含单个逻辑进程中的所有单独提交,而不是合并。这使得以后查看更改更加容易。想象一下,如果50名开发人员一直在合并各种功能,那么你会发现很难回顾开发历史。

也就是说,如果您已经将正在处理的分支推到了上游,那么不应该重新创建基础,而应该合并。对于尚未向上游推送的分支,请重新基址、测试和合并。

另一次您可能需要重新设置基值,是在向上游推送之前从分支中删除提交。例如:早期引入一些调试代码的提交和进一步清理代码的提交。实现这一点的唯一方法是执行交互式rebase:gitrebase-i<branch/commit/tag>

更新:当您使用Git连接到不支持非线性历史的版本控制系统(例如Subversion)时,还需要使用rebase。在使用git-svn桥时,非常重要的是,您合并回Subversion的更改是主干中最新更改之上的一系列更改。只有两种方法可以做到这一点:(1)手动重新创建更改;(2)使用rebase命令,这要快得多。

更新2:另一种考虑rebase的方法是,它可以从您的开发风格映射到您提交的存储库中接受的风格。你有一次提交来修复拼写错误,一次提交去清除未使用的代码等等。当你完成你需要做的事情时,你有一系列的提交。现在让我们假设您提交的存储库鼓励大量提交,因此对于您正在进行的工作,可能需要一次或两次提交。如何获取提交字符串并将其压缩到预期值?你可以使用一个交互式的rebase,将你的小提交压缩成更少的大块。如果需要相反的方式,情况也是如此——如果您的风格是几个大的提交,但存储库需要长串的小提交。你也可以使用rebase来实现这一点。如果您进行了合并,那么现在已经将提交样式移植到了主存储库中。如果有很多开发人员,你可以想象一段时间后,用几种不同的提交样式来跟踪历史会有多困难。

更新3:成功重新创建数据库后,是否仍需要合并?是的,你是这样做的。原因是重基基本上涉及到提交的“转移”。如上所述,这些提交是经过计算的,但如果从分支点开始有14个提交,那么假设您的重基没有任何问题,那么在重基完成后,您将提前14个提交(在您重基的点之前)。你在重新开始之前有一个分支。之后将有一个相同长度的分支。您仍然需要在发布更改之前进行合并。换言之,尽可能多次地重新设置基准(同样,只有在您没有将更改推到上游的情况下)。仅在重新基准后合并。

虽然合并绝对是集成更改的最简单和最常见的方式,但它不是唯一的方式: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的缺点也是一个很好的解读。

我什么时候使用git rebase?几乎不会,因为它改写了历史。gitmerge几乎总是首选,因为它尊重项目中实际发生的事情。

Pro Git的书在重新设置基础页面上有一个很好的解释。

基本上,合并将进行两次提交并合并它们。

一个重基将转到这两者的共同祖先,并在彼此之上逐步应用更改。这使得历史更加“清晰”和线性。

但当您重新创建基准时,您将放弃以前的提交并创建新的提交。因此,您永远不应该重新设置公共存储库的基础。其他在存储库中工作的人会恨你。

仅出于这个原因,我几乎完全合并了。99%的时候,我的分支机构没有太大的差异,所以如果有冲突,只会发生在一两个地方。

简短版本

合并在一个分支中接受所有更改,并在一次提交中将它们合并到另一个分支。Rebase说我希望我的分支点转移到一个新的起点

那你什么时候用这两种?

合并

假设您创建了一个分支来开发单个功能。当您想将这些更改带回master时,可能需要合并。

回扣

第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能希望从存储库中提取并重新创建基础,以将当前版本的更改作为基础。

挤压:在这两种情况下都会保留所有提交(例如:“add feature”,然后是“typ”,然后“oops typ again”…)。通过挤压,可以将提交合并为单个提交。挤压可以作为合并或重基操作(--squash标志)的一部分完成,在这种情况下,通常称为挤压合并或挤压重基。

拉取请求:流行的git服务器(Bitbucket、GitLab、GitHub等)允许配置如何在每个回购基础上合并拉取请求。按照惯例,UI可能会显示“合并”按钮,但该按钮可以使用任何标志(关键字:合并、重基、挤压、快进)执行任何操作。