建议何时使用Git rebase与Git merge?
成功重新创建数据库后,是否仍需要合并?
建议何时使用Git rebase与Git merge?
成功重新创建数据库后,是否仍需要合并?
当前回答
简短版本
合并在一个分支中接受所有更改,并在一次提交中将它们合并到另一个分支。Rebase说我希望我的分支点转移到一个新的起点
那你什么时候用这两种?
合并
假设您创建了一个分支来开发单个功能。当您想将这些更改带回master时,可能需要合并。
回扣
第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能希望从存储库中提取并重新创建基础,以将当前版本的更改作为基础。
挤压:在这两种情况下都会保留所有提交(例如:“add feature”,然后是“typ”,然后“oops typ again”…)。通过挤压,可以将提交合并为单个提交。挤压可以作为合并或重基操作(--squash标志)的一部分完成,在这种情况下,通常称为挤压合并或挤压重基。
拉取请求:流行的git服务器(Bitbucket、GitLab、GitHub等)允许配置如何在每个回购基础上合并拉取请求。按照惯例,UI可能会显示“合并”按钮,但该按钮可以使用任何标志(关键字:合并、重基、挤压、快进)执行任何操作。
其他回答
这很简单。使用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相同的提交,再加上具有新特性的提交,因此合并将是一个快速前进。
TL;博士
如果您有任何疑问,请使用merge。
简短回答
重新基准和合并之间的唯一区别是:
生成的历史树结构(通常只有在查看提交图时才明显)不同(一个有分支,另一个没有分支)。合并通常会创建一个额外的提交(例如树中的节点)。合并和重新基础将以不同的方式处理冲突。Rebase将一次提交一个冲突,而merge将一次显示所有冲突。
因此,简单的答案是根据您希望的历史外观选择重新基准或合并。
长答案
在选择要使用的操作时,需要考虑以下几个因素。
您从中获得更改的分支是否与团队之外的其他开发人员共享(例如,开源、公共)?
如果是,请不要重新设置基准。Rebase会破坏分支,除非他们使用git pull-Rebase,否则这些开发人员将拥有损坏/不一致的存储库。这是一个很快让其他开发人员感到不安的好方法。
您的开发团队有多熟练?
回扣是一种破坏性操作。这意味着,如果不正确应用它,您可能会丢失已提交的工作和/或破坏其他开发人员存储库的一致性。
我曾在团队中工作过,这些团队中的开发人员都来自一个公司能够负担得起专门的员工来处理分支和合并的时代。这些开发人员对Git了解不多,也不想了解太多。在这些团队中,我不会因为任何原因而冒险建议重新定基。
分支本身是否表示有用的信息
一些团队使用每个功能的分支模型,其中每个分支表示一个功能(或错误修复或子功能等)。在这个模型中,分支帮助识别相关提交的集合。例如,可以通过恢复该分支的合并来快速恢复功能(公平地说,这是一个罕见的操作)。或者通过比较两个分支来区分一个特性(更常见)。Rebase会破坏分支,这并不简单。
我也曾在使用每个开发人员分支模型的团队中工作过(我们都去过)。在这种情况下,分支本身不传递任何附加信息(提交已经包含作者)。换基不会有什么害处。
是否出于任何原因要还原合并?
与恢复合并相比,恢复(如撤消)重基相当困难和/或不可能(如果重基发生冲突)。如果您认为有机会恢复,请使用merge。
你是团队成员吗?如果是的话,你愿意在这一分支机构采取“要么全有要么全无”的方法吗?
需要使用相应的git pull--Rebase来拉动Rebase操作。如果你是自己工作,你可能会记得在适当的时候应该使用哪一个。如果你在一个团队中工作,这将很难协调。这就是为什么大多数rebase工作流建议对所有合并使用rebase(以及git pull--rebase对所有合并)。
常见的神话
合并销毁历史记录(压缩提交)
假设您有以下合并:
B -- C
/ \
A--------D
有些人会说合并“破坏”了提交历史,因为如果只查看主分支(A-D)的日志,就会错过B和C中包含的重要提交消息。
如果这是真的,我们就不会有这样的问题了。基本上,除非您明确要求不要看到B和C(使用--firstparent),否则您将看到它们。这很容易自己尝试。
Rebase允许更安全/更简单的合并
这两种方法的合并方式不同,但并不清楚其中一种总是优于另一种,这可能取决于开发人员的工作流程。例如,如果开发人员倾向于定期提交(例如,当他们从工作过渡到家庭时,他们可能一天提交两次),那么给定分支可能会有很多提交。其中许多提交看起来可能与最终产品不太相似(我倾向于对每个特性进行一次或两次重构)。如果其他人正在处理一个相关的代码领域,他们试图重新调整我的更改,这可能是一个相当乏味的操作。
热巴更酷/更性感/更专业
如果您想将rm别名为rm-rf以“节省时间”,那么可能需要重新设置基准。
我的两美分
我一直认为有一天我会遇到这样一个场景,Git rebase是解决这个问题的绝佳工具。就像我认为我会遇到这样一个场景,Git reflog是一个很好的工具,可以解决我的问题。我已经在Git工作了五年多了。这还没有发生。
混乱的历史对我来说从来都不是一个问题。我从来没有像读一本令人兴奋的小说那样阅读我的承诺历史。大多数时候,我需要一段历史,无论如何,我都会使用Git责备或Git平分。在这种情况下,合并提交实际上对我很有用,因为如果合并引入了问题,那对我来说就是有意义的信息。
更新(2017年4月)
我觉得有义务提及我个人对使用rebase的态度有所软化,尽管我的一般建议仍然有效。我最近与Angular 2 Material项目进行了很多互动。他们使用rebase来保持非常干净的提交历史。这使我能够非常容易地看到是什么提交修复了给定的缺陷,以及该提交是否包含在发布中。它是正确使用rebase的一个很好的例子。
Pro Git的书在重新设置基础页面上有一个很好的解释。
基本上,合并将进行两次提交并合并它们。
一个重基将转到这两者的共同祖先,并在彼此之上逐步应用更改。这使得历史更加“清晰”和线性。
但当您重新创建基准时,您将放弃以前的提交并创建新的提交。因此,您永远不应该重新设置公共存储库的基础。其他在存储库中工作的人会恨你。
仅出于这个原因,我几乎完全合并了。99%的时候,我的分支机构没有太大的差异,所以如果有冲突,只会发生在一两个地方。
我刚刚用自己的话为我的团队创建了一个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时减少合并冲突的提示:
经常退款。我通常建议每天至少做一次。尽量将同一行中的更改压缩为一次提交。
为了补充TSamper提到的我自己的答案,
在合并之前,重新创建数据库通常是一个好主意,因为这样做的目的是在分支Y中集成将要合并的分支B的工作。但同样,在合并之前,您要解决分支中的任何冲突(即:“rebase”,如“从分支B的最近一点开始在分支中回放我的工作”)。如果操作正确,后续从分支到分支B的合并可以快速进行。合并直接影响目标分支B,这意味着合并最好是微不足道的,否则分支B可能很长时间才能恢复到稳定状态(是时候解决所有冲突了)
重新基础后合并的意义?
在我描述的情况下,我将B重新放在我的分支上,只是为了有机会从B的最近一点回放我的工作,但同时留在我的分支。在这种情况下,仍然需要合并才能将我的“回放”工作带到B上。
另一种场景(例如Git Ready中描述的)是通过重基(rebase)将您的工作直接带到B中(这会保留所有漂亮的提交,甚至会给您机会通过交互式重基重新排序)。在这种情况下(当您在B分支中时重新创建基础),您是正确的:不需要进一步合并:
当我们没有合并或重新基础时,默认情况下是Git树
我们通过重新定基获得:
第二个场景的全部内容是:我如何将新功能恢复到主功能。
通过描述第一个重基场景,我的目的是提醒大家,重基也可以作为实现这一目标的初步步骤(即“将新功能重新引入主功能”)。您可以使用rebase首先将master“带入”新特性分支:rebase将重放来自HEAD master的新特性提交,但仍在新特性分支中,有效地将分支起点从旧的master提交移动到HEAD master。这允许您解决分支中的任何冲突(即,隔离,同时如果冲突解决阶段花费太长时间,则允许主分支继续并行发展)。然后,您可以切换到主功能并合并新功能(如果您想保留在新功能分支中完成的提交,则可以将新功能重新放到主功能上)。
So:
“rebase与merge”可以被视为两种方法来导入一个工作,例如master。但“先重新创建基础,然后合并”可以是一个有效的工作流,它可以先孤立地解决冲突,然后再恢复工作。