乐闻世界logo
搜索文章和话题

When do you use git rebase instead of git merge

8 个月前提问
4 个月前修改
浏览次数94

6个答案

1
2
3
4
5
6

Git rebaseGit merge 都是Git中用于合并不同分支上的更改的命令,但它们以不同的方式处理合并操作。

Git merge:

  • merge 命令通常用于将两个不同的分支合并到一起。
  • 当你执行 git merge 时,Git会创建一个新的“合并提交”(merge commit),该提交具有两个父提交:一个是当前分支的最后一个提交(HEAD),另一个是被合并分支的最后一个提交。
  • 合并提交的出现保留了项目历史的真实性,显示了一个分支合并到另一个分支的时间点。
  • 合并是一个非破坏性操作,即它不会改变已有分支的历史。

Git rebase:

  • rebase 命令用于将一个分支的更改重新应用于另一个分支上。
  • 当你执行 git rebase 时,Git会将你正在工作的分支的提交“转移”到目标分支的顶端。
  • 这种操作会改写历史,因为它实际上是重新创建了那些提交,就好像你是在目标分支的当前状态下重新做了那些工作。
  • rebase 可以创造出一条更干净、线性的历史,这样当查看项目历史的时候,就像是按照时间顺序一步步地发生的。

例子: 假设你在 feature 分支上工作,并且需要将 master 分支的最新更改集成到你的 feature 分支中。你可以选择 mergerebase 来实现这个目的。

如果你选择 merge,那么Git会创建一个新的合并提交,使得 feature 分支包含了 master 分支的所有更改。这个合并提交会有两个父提交,一个指向 feature 分支的最后一个提交,另一个指向 master 分支的最后一个提交。

如果你选择 rebase,Git会将你 feature 分支上的每个提交重新应用到 master 分支的最新提交之上。结果是你的 feature 分支看起来就像是在 master 分支的最新提交之后才开始的,这就创造了一条干净的、没有分叉的历史线。但是需要注意的是,如果 feature 分支上的更改与 master 分支上的更改有冲突,你需要在 rebase 过程中手动解决这些冲突。

2024年6月29日 12:07 回复

简洁版本

  • 合并将一个分支中的所有更改合并到一次提交中的另一个分支中。
  • Rebase 说我希望我分支的点移动到一个新的起点

那么什么时候使用其中任何一个呢?

合并

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

变基

  • 第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能想要拉取然后重新设置基础以从存储库中的当前版本进行更改。

挤压:在两种情况下都会保留所有提交(例如:“添加功能”,然后“拼写错误”,然后“哎呀再次拼写错误”...)。可以通过压缩将提交合并为单个提交。挤压可以作为合并或变基操作的一部分来完成(--squash 标志),在这种情况下,它通常称为挤压合并或挤压变基。

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

2024年6月29日 12:07 回复

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

例如,如果你有一个分支master,你创建一个分支来实现一个新功能,并说你给它命名cool-feature,当然,主分支是你新功能的基础。

现在,在某个时刻,您想要添加在master分支中实现的新功能。您可以切换到master并合并cool-feature分支:

shell
$ git checkout master $ git merge cool-feature

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

shell
$ git checkout cool-feature $ git rebase master

然后将其合并到master

shell
$ git checkout master $ git merge cool-feature

这次,由于主题分支具有与 master 相同的提交以及具有新功能的提交,因此合并将只是快进。

2024年6月29日 12:07 回复

长话短说

如果您有任何疑问,请使用合并。

简答

变基和合并之间的唯一区别是:

  • 历史记录的结果树结构(通常仅在查看提交图时才明显)是不同的(一个有分支,另一个没有)。
  • 合并通常会创建一个额外的提交(例如树中的节点)。
  • 合并和变基将以不同的方式处理冲突。Rebase 会一次呈现一个提交的冲突,而合并会一次呈现所有冲突。

因此,简短的答案是_根据您希望的历史记录选择变基或合并_。

长答案

选择使用哪种操作时应考虑几个因素。

您正在获取更改的分支是否与团队外部的其他开发人员共享(例如开源、公共)?

如果是这样,请不要变基。Rebase 会破坏分支,那些开发人员将拥有损坏/不一致的存储库,除非他们使用git pull --rebase. 这是快速让其他开发人员感到不安的好方法。

您的开发团队技术如何?

Rebase 是一种破坏性操作。这意味着,如果您没有正确应用它,您可能会丢失已提交的工作和/或破坏其他开发人员存储库的一致性。

我曾工作过的团队中,开发人员都来自公司有能力聘请专门人员来处理分支和合并的时代。那些开发人员对 Git 不太了解,也不想了解太多。在这些团队中,我不会出于任何原因冒险推荐变基。

分支本身是否代表有用的信息

一些团队使用每个功能分支模型,其中每个分支代表一个功能(或错误修复,或子功能等)。在此模型中,分支有助于识别相关提交集。例如,可以通过恢复该分支的合并来快速恢复某个功能(公平地说,这是一种罕见的操作)。或者通过比较两个分支来区分特征(更常见)。变基会破坏分支,这并不简单。

我还曾在使用每个开发人员分支模型的团队中工作过(我们都经历过)。在这种情况下,分支本身不传达任何附加信息(提交已经有作者)。重新设定基准不会有什么坏处。

您可能出于某种原因想要恢复合并吗?

与恢复合并相比,恢复(如撤消)变基相当困难和/或不可能(如果变基有冲突)。如果您认为有可能想要恢复,请使用合并。

你在团队中工作吗?如果是这样,您愿意在这个分支上采取全有或全无的方法吗?

Rebase 操作需要使用相应的git pull --rebase. 如果您自己工作,您也许能够记住应该在适当的时间使用哪个。如果你在一个团队中工作,这将很难协调。git pull --rebase这就是为什么大多数变基工作流程建议对所有合并(以及所有拉取)使用变基。

常见的误区

合并会破坏历史(压缩提交)

假设您有以下合并:

shell
B -- C / \ A--------D

有些人会说合并“破坏”了提交历史记录,因为如果您只查看主分支(A - D)的日志,您将错过 B 和 C 中包含的重要提交消息。

如果这是真的,我们就不会有这样的问题了。基本上,您会看到 B 和 C,除非您明确要求不要看到它们(使用 --first-parent)。这很容易自己尝试。

Rebase 允许更安全/更简单的合并

这两种方法的合并方式不同,但尚不清楚一种方法总是优于另一种方法,这可能取决于开发人员的工作流程。例如,如果开发人员倾向于定期提交(例如,当他们从工作地点转移到家庭时,他们可能每天提交两次),那么对于给定分支可能会有大量提交。其中许多提交可能看起来与最终产品完全不同(我倾向于对每个功能重构我的方法一次或两次)。如果其他人正在处理相关的代码区域,并且他们试图重新调整我的更改,那么这可能是一个相当乏味的操作。

Rebase 更酷/更性感/更专业

如果您喜欢使用别名rmrm -rf“节省时间”,那么也许 rebase 适合您。

我的两分钱

我总是认为有一天我会遇到一个场景,Git rebase 是解决问题的很棒的工具。就像我认为我会遇到这样一个场景:Git reflog 是一个很棒的工具,可以解决我的问题。我使用 Git 已有五年多了。这还没有发生。

混乱的历史对我来说从来都不是问题。我从来不会像读一本令人兴奋的小说那样阅读我的提交历史。大多数时候我需要历史记录,无论如何我都会使用 Git Blame 或 Git Bisect。在这种情况下,合并提交实际上对我来说很有用,因为如果合并引入了问题,那对我来说就是有意义的信息。

更新(4/2017)

我觉得有必要提一下,尽管我的一般建议仍然有效,但我个人对使用 rebase 已经软化了。我最近与Angular 2 Material项目进行了很多互动。他们使用 rebase 来保持非常干净的提交历史记录。这使我能够非常轻松地查看哪些提交修复了给定的缺陷以及该提交是否包含在版本中。它是正确使用 rebase 的一个很好的例子。

2024年6月29日 12:07 回复

我刚刚用我自己的话为我的团队创建了一个常见问题解答来回答这个问题。让我分享一下:

什么是merge?

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

什么是rebase?

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

merge和 之间的主要区别是什么rebase

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

在什么情况下我们应该使用merge?

merge每当您想要将分支分支的更改添加基本分支时即可使用。

通常,您可以通过单击拉取/合并请求(例如 GitHub 上的)上的“合并”按钮来执行此操作。

在什么情况下我们应该使用rebase?

rebase每当您想要将基础分支的更改添加回分支分支时即可使用。

通常,feature只要分支发生更改,您就可以在分支中执行此操作main

为什么不使用merge将基础分支的更改合并到功能分支中?

  1. git 历史记录将包含许多不必要的合并提交。如果一个功能分支中需要多次合并,那么该功能分支甚至可能容纳比实际提交更多的合并提交!

  2. 这会创建一个循环,破坏 Git 设计的思维模型,从而导致 Git 历史记录的任何可视化出现问题。

    想象有一条河(例如“尼罗河”)。水朝一个方向流动(Git 历史上的时间方向)。时不时地,想象那条河有一条分支,并假设大部分分支又汇回到河流中。这就是河流自然流动的样子。这说得通。

    但想象一下那条河有一个小支流。然后,由于某种原因,河流汇入支流,支流从那里继续。从技术上讲,这条河现在已经消失了,它现在处于支流中。但后来,不知何故,那根树枝神奇地又融入了河流。你问哪条河?我不知道。河流现在实际上应该在分支中,但不知何故它仍然继续存在,我可以将分支合并回河流中。所以说,河就是河。有点没有道理。

    这正是当您将merge基础分支合并到一个feature分支中,然后当feature分支完成时,您再次将其合并回基础分支时所发生的情况。心智模式被打破了。因此,您最终得到的分支可视化效果并不是很有帮助。

使用时的 Git 历史记录示例merge

使用合并时的 Git 历史记录示例

请注意许多以Merge branch 'main' into .... 如果您变基,它们甚至不存在(在那里,您将只有拉取请求合并提交)。还有许多视觉分支合并循环(maininto featureinto main)。

使用时的 Git 历史记录示例rebase

使用 rebase 时的 Git 历史记录示例

更干净的 Git 历史记录,更少的合并提交,并且没有任何混乱的可视分支合并循环。

有什么缺点/陷阱吗rebase

是的:

  1. 因为rebase移动提交(技术上重新执行它们),所以所有移动提交的提交日期将是变基时间,并且git 历史记录可能看起来丢失了初始提交时间。因此,如果由于某种原因所有工具都需要提交的确切日期,那么这merge是更好的选择。但通常情况下,干净的 git 历史记录比确切的提交日期有用得多。并且作者日期字段将在需要时继续保留原始提交日期。

  2. 如果变基分支有多个提交更改了同一行,并且该行也在基础分支中进行了更改,则您可能需要多次解决同一行的合并冲突,而合并时则不需要这样做。因此,平均而言,需要解决更多的合并冲突。

请注意,有一个常见的误解,即即使多次编辑rebase同一行,也会导致更多合并冲突。这是因为执行了多个提交,因此,例如,在单个提交中出现的 10 个冲突会分布在这些多个提交中。因此,您会更频繁地收到 Git 客户端的“存在合并冲突”消息,但冲突总数仍然相同(除非您在分支中多次更改同一行)。merge

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

  1. 经常变基。我通常建议每天至少进行一次。
  2. 尝试将同一行上的更改尽可能压缩到一次提交中。
2024年6月29日 12:07 回复

为了补充TSamper提到的我自己的答案

  • 在合并之前进行变基通常是一个好主意,因为这个想法是将要合并的分支Y的工作集成到您的分支B中。
    但同样,在合并之前,您需要解决分支中的任何冲突_(_即:“rebase”,如“从分支的最近点开始重播我的分支中的工作B)。
    如果操作正确,后续从分支合并到分支B可以快进。

  • 合并直接影响目标分支B,这意味着合并最好是微不足道的,否则该分支B可能需要很长时间才能恢复到稳定状态(解决所有冲突的时间)


变基后合并的点是什么?

在我描述的情况下,我重新定位B到我的分支,只是为了有机会从最近的点重播我的工作B,但同时留在我的分支中。
在这种情况下,仍然需要合并才能将我的“重播”工作带到B.

另一种情况(例如,在 Git Ready 中描述B)是通过变基直接引入您的工作(这确实保留了所有好的提交,甚至让您有机会通过交互式变基重新排序它们)。
在这种情况下(在 B 分支中进行变基),你是对的:不需要进一步合并:

当我们没有合并或重新建立基础时,默认的 Git 树

变基1

我们通过变基得到:

变基3

第二个场景是关于:如何将新功能返回到 master 中。

我的观点是,通过描述第一个 rebase 场景,提醒大家,rebase 也可以用作实现此目的的初步步骤(即“将新功能恢复到 master 中”)。
您可以使用 rebase 首先将 master “引入”新功能分支:rebase 将重播来自 的新功能提交HEAD master,但仍在新功能分支中,从而有效地将分支起点从旧的 master 提交移动到HEAD-master
这允许您解决分支中的任何冲突_(_意味着,单独地,同时如果您的冲突解决阶段花费太长时间,则允许 master 继续并行发展)。
然后,您可以切换到 master 并合并new-feature(或者如果您想保留分支中完成的提交,则重新建立new-feature基础)。master``new-feature

所以:

  • “变基与合并”可以被视为导入作品的两种方式,例如,master.
  • 但“变基然后合并”可以是一个有效的工作流程,首先单独解决冲突,然后恢复工作。
2024年6月29日 12:07 回复

你的答案