git之rebase

1,106 阅读12分钟

首发

自然博客:hsslive.cn/article/90

rebase是什么

rebase翻译过来是变基,通俗的讲就是改变这个commit的基准点,传统的merge是会找到两个分支共同的祖先节点然后进行三方合并的,合并的结果就是生成了一个新的提交,而且是分叉的。而rebase则是找到这两个分支的最近共同祖先节点,然后找到差异,将差异全都移到另一个分支上,因此这就不会分叉。merge和rebase的结果是一致的,只不过是历史提交的记录不一样而已。

当前状态

git status

注意:这三次提交每次修改的都是不同的文件(如:修改音乐接口这个提交只修改了a文件,修改分类接口这个提交只修改了b文件,修改标签这个提交只修改了c文件,这三次修改的文件都没有交集。)

➜  vue3-blog-admin git:(master) git status 
位于分支 master
您的分支领先 'origin/master' 共 3 个提交。
  (使用 "git push" 来发布您的本地提交)

无文件要提交,干净的工作区

git log

注意:这三次提交每次修改的都是不同的文件(如:修改音乐接口这个提交只修改了a文件,修改分类接口这个提交只修改了b文件,修改标签这个提交只修改了c文件,这三次修改的文件都没有交集。)

➜  vue3-blog-admin git:(master) git log 
commit f45bd6b95577c875ae19112ca98fad361c52d8f1 (HEAD -> master)
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:49:12 2022 +0800

    fix: 修改音乐接口

commit bb22f385bc566803cdcf576cada1a5baf6ecc53f
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:45:42 2022 +0800

    fix: 修改分类接口

commit dd8e545ca8a0f34d8f76138c19dd1b047f4f5c49
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:44:10 2022 +0800

    fix: 修改标签接口

commit 6427a57572fc382e0675d63ded24974a3bb59599 (origin/master, origin/HEAD)
Author: shuisheng <2274751790@qq.com>
Date:   Mon Apr 25 22:15:48 2022 +0800

    fix: 优化已知问题

git reflog

注意:这三次提交每次修改的都是不同的文件(如:修改音乐接口这个提交只修改了a文件,修改分类接口这个提交只修改了b文件,修改标签这个提交只修改了c文件,这三次修改的文件都没有交集。)

f45bd6b (HEAD -> master) HEAD@{61}: commit: fix: 修改音乐接口
bb22f38 HEAD@{62}: commit: fix: 修改分类接口
dd8e545 HEAD@{63}: commit: fix: 修改标签接口
6427a57 (origin/master, origin/HEAD) HEAD@{64}: clone: from https://github.com/galaxy-s10/vue3-blog-admin.git

合并同一个分支里的commit

当前在功能分支,因为有的提交其实没改什么东西,比如改个样式,删个注释之类的都commit一次,导致一个很简单的需求commit的次数太多了,希望把一些没什么太大作用的commit进行合并,就可以用rebase。

还有一个小功能,如果你不小心把秘钥之类的数据commit了,可以在这个commit后面把这些数据删掉,然后再rebase合并掉这个有秘钥的commit,这样提交的commit记录里面就不会出现之前有秘钥的那个commit。

测试

git rebase -i,可以看到这里的顺序和我们git log和reflog的顺序是相反的。

➜  vue3-blog-admin git:(master) git rebase -i
pick dd8e545 fix: 修改标签接口
pick bb22f38 fix: 修改分类接口
pick f45bd6b fix: 修改音乐接口

# 变基 6427a57..f45bd6b 到 6427a57(3 个提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但编辑提交说明
# e, edit <提交> = 使用提交,但停止以便在 shell 中修补提交
# s, squash <提交> = 使用提交,但挤压到前一个提交
注释太多,省略了

测试一

改成如下:

pick dd8e545 fix: 修改标签接口
pick bb22f38 fix: 修改分类接口
s f45bd6b fix: 修改音乐接口

然后保存(:wq),后面遇到的交互都不修改直接保存(:wq),最终的结果:

[分离头指针 25c7688] fix: 修改分类接口
 Date: Sat Apr 30 10:45:42 2022 +0800
 4 files changed, 18 insertions(+), 10 deletions(-)
成功变基并更新 refs/heads/master。

git log:

commit 25c768819e98257df46d82b308e814cd2a572a01 (HEAD -> master)
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:45:42 2022 +0800

    fix: 修改分类接口
    
    fix: 修改音乐接口

commit dd8e545ca8a0f34d8f76138c19dd1b047f4f5c49
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:44:10 2022 +0800

    fix: 修改标签接口

commit 6427a57572fc382e0675d63ded24974a3bb59599 (origin/master, origin/HEAD)
Author: shuisheng <2274751790@qq.com>
Date:   Mon Apr 25 22:15:48 2022 +0800

    fix: 优化已知问题

可以看出,和最初的git log对比,修改分类和音乐的提交都没了,这两个提交合并成了一个新的commit,并且head指向了这个新的commit。

测试二

首先,回滚到最初的commit,然后改成如下:

➜  vue3-blog-admin git:(master) git rebase -i
pick dd8e545 fix: 修改标签接口
s bb22f38 fix: 修改分类接口
pick f45bd6b fix: 修改音乐接口

然后保存(:wq),后面遇到的交互都不修改直接保存(:wq),最终的结果:

[分离头指针 89cd7b8] fix: 修改标签接口
 Date: Sat Apr 30 10:44:10 2022 +0800
 5 files changed, 16 insertions(+), 33 deletions(-)
成功变基并更新 refs/heads/master。

git log:

➜  vue3-blog-admin git:(master) git log 
commit 7189af3e3359f497fe878449f949d14a8e703c44 (HEAD -> master)
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:49:12 2022 +0800

    fix: 修改音乐接口

commit 89cd7b8714ea9926ae43cc75f28fbdbc8fb4c61d
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:44:10 2022 +0800

    fix: 修改标签接口
    
    fix: 修改分类接口

commit 6427a57572fc382e0675d63ded24974a3bb59599 (origin/master, origin/HEAD)
Author: shuisheng <2274751790@qq.com>
Date:   Mon Apr 25 22:15:48 2022 +0800

    fix: 优化已知问题

可以看出,和最初的git log对比,之前的三个提交都没了,标签和分类提交合并成了一个新的commit,而且音乐接口的commit也变了,并且head指向了音乐接口commit

测试三

首先,回滚到最初的commit,然后改成如下:

➜  vue3-blog-admin git:(master) git rebase -i
s dd8e545 fix: 修改标签接口
pick bb22f38 fix: 修改分类接口
pick f45bd6b fix: 修改音乐接口

然后保存(:wq),结果却报错了:

error: 没有父提交的情况下不能 'squash'
您可以用 'git rebase --edit-todo' 修正,然后执行 'git rebase --continue'。
或者您可以用 'git rebase --abort' 终止变基。

其实从上面的测试一和二就可以看出来,使用s(squash的缩写),会将这个提交合并到它的上一个提交,但是很明显,第一个修改标签的提交上面没有提交了,因此会报错,所以这里直接用 'git rebase --abort' 终止变基。如么该怎么解决,其实可以通过换顺序解决,我们把修改标签接口的位置换到分类接口下面:

➜  vue3-blog-admin git:(master) git rebase -i
pick bb22f38 fix: 修改分类接口
s dd8e545 fix: 修改标签接口   
pick f45bd6b fix: 修改音乐接口

然后继续保存,最终结果:

[分离头指针 b9061ae] fix: 修改分类接口
 Date: Sat Apr 30 10:45:42 2022 +0800
 5 files changed, 16 insertions(+), 33 deletions(-)
成功变基并更新 refs/heads/master。

再次git log:

➜  vue3-blog-admin git:(master) git log 
commit 4089bb3631803588bebd2ca7b01a4f3fbab2ac79 (HEAD -> master)
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:49:12 2022 +0800

    fix: 修改音乐接口

commit b9061ae426a13535cc8e354c8fffbba2ad61abce
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:45:42 2022 +0800

    fix: 修改分类接口
    
    fix: 修改标签接口

commit 6427a57572fc382e0675d63ded24974a3bb59599 (origin/master, origin/HEAD)
Author: shuisheng <2274751790@qq.com>
Date:   Mon Apr 25 22:15:48 2022 +0800

    fix: 优化已知问题

可以看出,和最初的git log对比,之前的三个提交都没了,标签和分类提交合并成了一个新的commit,而且音乐接口的commit也变了,并且head指向了音乐接口commit

小结

因为是同一个分支里面合并commit,这些commit的共同祖先其实都是同一个,因此,rebase不会存在冲突,而rebase时的顺序其实也无关紧要,最终rebase后的代码其实都是一样的。

合并不同分支的commit

因为合并不同分支的commit,可能会存在他们的共同祖先不一样,因此rebase期间可能会存在冲突,需要解决冲突。

➜  vue3-blog-admin git:(master) git log 
commit f45bd6b95577c875ae19112ca98fad361c52d8f1 (HEAD -> master)
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:49:12 2022 +0800

    fix: 修改音乐接口

commit bb22f385bc566803cdcf576cada1a5baf6ecc53f
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:45:42 2022 +0800

    fix: 修改分类接口

commit dd8e545ca8a0f34d8f76138c19dd1b047f4f5c49
Author: 黄水生 <2274751790@qq.com>
Date:   Sat Apr 30 10:44:10 2022 +0800

    fix: 修改标签接口

commit 6427a57572fc382e0675d63ded24974a3bb59599 (origin/master, origin/HEAD)
Author: shuisheng <2274751790@qq.com>
Date:   Mon Apr 25 22:15:48 2022 +0800

    fix: 优化已知问题

假设现在的功能分支叫dev,这个功能分支是基于主分支的6427a57572fc382e0675d63ded24974a3bb59599提交分叉出来的,等到我们开发完成后,需要把我们的代码提交到远程仓库再合并到主分支(master),因此为了避免我们当前功能分支的代码提交上去远程仓库合并到主分支的时候出现冲突,我们先切回到主分支:

➜  vue3-blog-admin git:(dev) git checkout master 
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。

然后git pull一下拉最新的代码下来:

➜  vue3-blog-admin git:(master) git pull
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 4), reused 0 (delta 0), pack-reused 0
展开对象中: 100% (7/7), 967 字节 | 193.00 KiB/s, 完成.
来自 https://github.com/galaxy-s10/vue3-blog-admin
   6427a57..82cc934  master     -> origin/master
更新 6427a57..82cc934
Fast-forward
 src/views/music/list/index.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

果然,线上的代码有改动了,如果线上的改动的代码和我们dev分支改动了同一行代码,那么rebase的时候就会报冲突,先切回去dev分支,rebase试试:

➜  vue3-blog-admin git:(master) git checkout dev
切换到分支 'dev'
➜  vue3-blog-admin git:(dev) git rebase master 
自动合并 src/views/music/list/index.vue
冲突(内容):合并冲突于 src/views/music/list/index.vue
error: 不能应用 90563e3... fix: 修改标签接口
提示:Resolve all conflicts manually, mark them as resolved with
提示:"git add/rm <conflicted_files>", then run "git rebase --continue".
提示:You can instead skip this commit: run "git rebase --skip".
提示:To abort and get back to the state before "git rebase", run "git rebase --abort".
不能应用 90563e3... fix: 修改标签接口
➜  vue3-blog-admin git:(82cc934) ✗ 

果然报错了,看看报错的代码:

// src/views/music/list/index.vue
await fetchUpdateMusic({
<<<<<<< HEAD
  ...res,
  created_at: undefined, //删除无用属性
  updated_at: undefined, //删除无用属性
  deleted_at: undefined, //删除无用属性
=======
  id: res.id,
  author: res.author,
  name: res.name,
  audio_url: res.audio_url,
  status: res.status,
  cover_pic: res.cover_pic,
>>>>>>> 90563e3 (fix: 修改标签接口)
});

原来是加了注释而已,但是后面我改了后更加的见名知意了,因此经过和更改代码的人一番沟通后,就把我的代码给用上了,解决完冲突后然后就很简单了,重新 git add . ,然后 git rebase --continue ,保存,就好了:

➜  vue3-blog-admin git:(82cc934) ✗ git add .
➜  vue3-blog-admin git:(82cc934) ✗ git rebase --continue 
[分离头指针 33f179d] fix: 修改标签接口
 7 files changed, 29 insertions(+), 38 deletions(-)
成功变基并更新 refs/heads/dev。
➜  vue3-blog-admin git:(dev) 

然后就可以愉快的提交代码了,查看提交记录,也是一条直线。

注意

如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

上面的官方文档的原话,其实大概就是指你提交到远程仓库的代码,如果有别人在用你的代码,那么请不要在本地rebase了线上已有的代码,并且git push -f上传到远程仓库!

参考

Git-分支-变基(官网)

bilibili-阿里云

bilibili-Git基本原理介绍(22)——什么是git rebase