git rebase使用教程 以及和merge的区别

发布时间:2023-08-31 09:19:25 作者:yexindonglai@163.com 阅读(501)

Merge和Rebase概念概述

rebase 和 merge 相似,但又不完全相同,本质上都是用来合并分支的命令,区别如下

  1. merge合并分支会多出一条merge commit记录,而rebase不会
  2. merge的提交树是非线性的,会有分叉,而rebase的提交树是线性的(通过重写提交历史),它是一条直线;
  3. rebase可以将多个commit合并,而merge不支持这个功能
  4. merge合并操作很友好,因为它没有破坏性。现存的分支历史不会发生什么改变,而rebase提交是线性的,虽然便于理解提交历史,却需要付出两种代价:安全性和可追溯性;

1、分支描述

请考虑这个场景,当你开始在一个专有的feature分支开发新的功能时,另一位团队成员更新了main分支的内容。这将会造成一个分叉的提交历史,接下来,我们将用merge和rebase来合并分支,并解释它们之间的区别;

2、使用merge合并

最简单的方法就是把main分支合并到feature分支:

  1. # 先切换到 feature 分支
  2. git checkout feature
  3. # 将main分支的内容合并到 feature 分支
  4. git merge main

或者用下面这样的单行命令:

  1. git merge feature main

这会在feature分支中创建一个合并提交,这次提交会连结两个分支的提交历史,在分支图示结构中看起来像下面这样:

合并操作很友好,因为它没有破坏性。现存的分支历史不会发生什么改变。只是新增了一个commit记录(绿色带*号的就是合并记录);

但是另一方面来说,这也意味着每当feature分支需要应用上游分支的更改时,都会在提交历史上增加一个无关的提交历史。如果main分支的更新非常活跃,这种操作也会对功能分支的提交历史产生相当程度的污染。虽然通过复杂的git log命令可以减轻这种提交历史的混乱现状,但仍然会让其他开发者对于提交历史感到费解。

3、使用rebase合并

为了替代merge操作,你也可以把feature分支的提交历史rebase到main分支的提交历史顶端:

  1. # 先切换到 feature 分支
  2. git checkout feature
  3. # 将main分支的内容合并到 feature 分支
  4. git rebase main

这些操作会把feature分支的起始历史放到main分支的最后一次提交之上,也达成了使用main分支中新代码的目的。但是,相对于merge操作中新建一个合并提交,rebase操作会通过为原始分支的每次提交创建全新的提交,从而重写原始分支的提交历史(绿色带*号部分就是被重写提交历史)。

使用rebase操作的最大好处在于你可以让项目提交历史变得非常干净整洁。首先,它消除了git merge操作所需创建的没有必要的合并提交。其次,正如上图所示,rebase会造就一个线性的项目提交历史——也就是说你可以从feature分支的顶部开始向下查找到分支的起始点,而不会碰到任何历史分叉。这在使用git log,git bisect以及gitk等命令时更简单。

不过为了获得这种便于理解的提交历史,却需要付出两种代价:安全性和可追溯性。如果不能遵循rebase的黄金法则,重写项目提交历史会为协作工作流程带来潜在的灾难性后果。再次,rebase操作丢失了合并提交能够提供的上下文信息——所以你就无法知道功能分支是什么时候应用了上游分支的变更。

可交互式rebase (将多个commit合并为一个)

可交互式rebase让你在把变更提交给其他分支之前有机会对提交记录进行修改。这甚至比自动rebase操作更强大,毕竟它提供了对于分支提交历史的完全掌控力。通常来说这一操作的使用场景在于合并功能分支到main分支之前,对于功能分支杂乱的提交记录进行整理。

进行可交互式rebase操作,需要向git rebase命令传递i选项参数

  1. git checkout feature
  2. git rebase -i main

执行以上命令会打开一个文本编辑器,其中内容为分支中需要移动的所有提交列表:

  1. pick 33d5b7a Message for commit #1
  2. pick 9480b3d Message for commit #2
  3. pick 5c67e61 Message for commit #3

上面这样的列表正表示了分支被rebase之后其历史的长相。通过修改pick命令或者对提交历史进行重新排序,你可以让最终的提交历史变成任何你希望的样子。比如说,如果第二次提交修复了第一次提交的什么BUG,你可以使用fixup命令替代pick来把两次提交压缩在一起。

  1. pick 33d5b7a Message for commit #1
  2. fixup 9480b3d Message for commit #2
  3. pick 5c67e61 Message for commit #3
指令说明
  • p,pick <提交>: 保留当前的提交内容。
  • r,reword <提交>: 保留当前的提交内容。,但修改提交说明
  • e,edit <提交>: 保留当前的提交内容,并允许你修改该提交的文件内容
  • s,squash <提交> : 将当前提交与前一个提交合并,并将它们合并为一个提交,允许你合并提交信息。
  • f,fixup <提交>: 与 squash 类似,但将当前提交合并到前一个提交中,并丢弃当前提交的提交信息。
  • x,exec <命令>: 执行 shell 命令,可以在 rebase 过程中执行自定义操作
  • b,break : 在当前提交之后创建一个新的提交点,将当前提交内容分成两个独立的提交。 (使用git rebase—continue’继续变基)
  • d,drop <提交>: 丢弃当前的提交内容,不保留该提交。
  • l,label <label>: 修改当前的提交信息,与 reword 类似,但它不会打开编辑器。

当你保存并关闭这个文件之后,Git会根据你的调改结果执行rebase操作,根据上面的例子项目历史会变成下图这样:

通过清除那些并不重要的提交历史可以让项目整体的历史更易读易懂。这一点是git merge操作所无法提供的。

关键字git