Swift学习16.1:git merge和git rebase的区别?

张建 lol

概述

首先我们要知道,git mergegit rebase 做的事情其实是一样的,它们被设计用来 将一个分支的更改 并入 另一个分支,只不过是方式有些不同

merge

我们知道,将 master 分支合并到 feature 分支最简单的办法就是用下面的两个命令:

1
2
3
4
// 切换到当前 feature 分支
git checkout feature
// 将 master 分支 合并到 当前分支
git merge master

或者,你也可以用一行命令实现上面的功能

1
git merge master feature

feature 分支中心的合并提交(merge commit)将两个分支的历史连在了一起,你会得到下面的分支结构:

merge 好处在于它是一个 安全的操作,现有的分支不会被更改,避免了 rebase 潜在的缺点;另一方面,意味着每次合并上游更改时,feature 分支都会引入一个外来的合并提交,如果 master 很活跃,或多或少会污染你的分支历史

什么是rebase?

rebase 在git中是一个非常有魅力的命令,使用得当会极大提高自己的工作效率;相反,如果乱用,会给团队中其他人带来麻烦。它的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用 rebase 命令可以使我们的提交历史干净、简洁!

不要通过rebase对任何已经提交到公共仓库中的commit进行修改(你自己一个人玩的分支除外)

rebase作用?

作为 merge替代选择,你可以用下面的命令 将 feature 分支并入 master 分支

1
2
git checkout feature
git rebase master

上面的命令会把整个 feature 分支 移动master 分支的 后面,有效的把所有 master 分支上新的提交 并入 过来。如下图:

由上图可以看出,先是逐个应用了 master 分支的更改,然后以 master 分支最后的提交作为 基点,再逐个应用 feature 的每个更改

注:
如果 rebase 过程中产生冲突,需要手动解决冲突,使用 git addn .、git rebase --continue 来解决冲突,完成 rebase
如果不想要某次 rebase 的结果,使用 git rebase --skip 来跳过这次 rebase

合并多个 commit 为一个完整 commit?

当我们在本地仓库中提交了多次,在我们把本地提交 push 到公共仓库中之前,为了让提交记录更简洁明了,我们希望把分支下 B、C、D三个提交记录合并 为一个完整的提交,然后再 push 到公共仓库。

现在我们模拟一个环境,在 master 分支上只有一次 commit 提交 A,在 zj_feature 分支上添加了 四次 commit 提交 A B C D

1
2
3
4
// 提交到暂缓区
git add .
// 提交到本地仓库
git commit -m "add 四次"

当我们提交了四次之后:

1
2
// 查看提交日志
git log

我们的目标是把 最后三个 B C D 提交合并为一个提交,这里我们使用命令:

1
git rebase -i [startpoint] [endpoint]

其中 -i 的意思是 --interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint] 指定了一个编辑区间,如果不指定 [endpoint],则该区间的终点默认是当前分支 HEAD 所指向的 commit(注:该区间指定的是一个 前开后闭 的区间)。

在查看 git log 日志后,我们运行以下命令:

1
2
// commit 前7位
git rebase -i de38103

1
git rebase -i HEAD~3

然后我们会看到如下界面:

上面未被注释的部分列出的是我们本次 rebase 操作包含的所有提交,下面注释部分是 git 为我们提供的命令说明。买一个 commit id 前面的 pick 表示指令类型,git 为我们提供了以下几个命令:

1
2
3
4
5
6
7
p:pick 保留该 commit 
r:reword 保留该 commit,但我需要修改该 commit 的注释
e:edit 保留该 commit,但我要停下来修改该提交(不仅仅修改注释)
s:squash 将该 commit 和 前一个 commit 合并
f:fixup 将该 commit 和 前一个 commit 合并,但我不要保留该提交的注释信息
x:exec 执行sehll命令
d:drop 我要丢弃该 commit

根据我们的需求,我们将 commit 内容编辑如下:

1
2
3
pick 06d4d03 add v2
squash a24aa92 add v3
squash 69827d3 add v4

然后是注释修改界面:

编辑完保存即可完成commit的合并了,我们用 git log 查看一下合并的记录:

将某一段commit粘贴到另一个分支上

当我们项目中存在多个分支,有时候我们需要将某一个分支中的一段提交同时应用到其他分支中,就像下图:

在实际模拟中,我们创建了 masterzj_feature 两个分支,我们在 zj_feature 分支上提交了 B、C、D、E、F 五次,如下图:

master分支:

zj_feature分支:

我们希望将 zj_feature 分支中的 C~E 部分 复制master 分支中,这时我们就可以通过 rebase 命令来实现(如果只是复制某一两个提交到其他分支,建议使用更简单的命令: git cherry-pick )。

我们使用命令的形式为:

1
git rebase [startpoint] [endpoint] --onto [branchName]

其中 [startpoint] [endpoint] 仍然和上一个命令一样指定了一个编辑区间(前开后闭),--onto 的意思是要 将该指定的提交复制到哪个分支上

1
2
// B-29541ef,E-1a2f8ab
git rebase 29541ef 1a2f8ab --onto master

注:因为 [startpoint] [endpoint] 指定的是一个 前开后闭 的区间,为了让这个区间包含 C 提交,我们将区间起始点向后退了一步。

我们发现报错:

补充知识点:

$ git rebase –abort
会放弃合并,回到 rebase 操作之前的状态,之前的提交不会丢弃
$ git rebase –skip
会讲引起冲突的 commits 丢弃掉
$ git rebase –continue
合并冲突,结合 git add 文件 命令一起用与修复冲突,提示开发者,一步一步地有没有解决冲突

出现上述的报错执行下面的命令:

1
2
3
4
5
mac@bogon RebaseDemo % git add .
mac@bogon RebaseDemo % git rebase --continue
dropping 1a2f8abb86f75de3ac5780324e907a18b16910e4 E -- patch contents already upstream
Successfully rebased and updated detached HEAD.
mac@bogon RebaseDemo %

运行完成后查看当前分支的日志:

可以看到,C~E 部分的提交内容 已经复制 到了 G 的后面了,大功告成?NO!我们看一下当前分支的状态:

当前 HEAD 处于游离状态,实际上,此时所有分支的状态应该是这样:

所以,虽然此时 HEAD 所指向的内容正是我们所需要的,但是 master 分支是没有任何变化的,git 只是将 C~E 部分的提交内容复制一份粘贴到了 master 所指向的提交后面,我们需要做的就是将 master 所指向的 提交id 设置为当前 HEAD 所指向的提交 id 就可以了,即:

1
2
3
mac@bogon RebaseDemo % git checkout master
mac@bogon RebaseDemo % git reset --hard 58e67df
HEAD is now at 58e67df F

此时我们查看一下当前分支和log日志:

此时我们才大功告成!

如果操作失误,回退到某个版本

1
git reset --hard 1a2f8ab

总结

  • 如果你想要一个 干净的、线性的提交历史,并且没有合并提交,你应该使用 git rebase

  • 如果你想要 保存项目完整的历史,并且 避免重写公共分支上的 commit,你应该使用 git merge

  • Post title:Swift学习16.1:git merge和git rebase的区别?
  • Post author:张建
  • Create time:2023-05-09 11:36:26
  • Post link:https://redefine.ohevan.com/2023/05/09/Swift/Swift学习16.1:git-merge和git-rebase的区别?/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.