Git 笔记 - git rebase

提醒:不要在多人合作的公共分支里使用 rebase,自己的开发分支除外。 在公共分支里使用 rebase 的话可能会影响其他小伙伴的工作噢!

01 基本用法

描述git rebase 可以理解为 重新设置基线 ,即 将当前分支重新设置开始点

原理解释:它需要基于一个分支(后面称该分支为公共分支)来设置当前分支的基线,将当前分支的开始时间轴 向后移动到 公共分支的最新跟踪提交最后面 ,这样当前分支就是公共分支的最新跟踪分支了。

白话文解释:rebase 会把当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像从最新公共分支又重新拉出来这个分支一样~

举个例子:现有一个 master 主分支,小明要开发新需求从 master 拉取并创建了名为 dev 的新分支。而当小明开发完后需要合并到 master,但发现 master 相比之前多了一些变更。这时想要同步 master 的改动应该怎么办呢?如下图所示:

dev
master
创建新分支 dev
G
F
B
E
D
C
A

讨论小剧场 01 ~
————
同学A:使用 git merge 就能解决这个问题。
小明:但提交记录会很乱,而且若之后要回滚这部分的代码还得考虑是否混杂涉及了别人的提交。
————
同学B:若只复制一两个提交到其他分支,建议使用更简单的命令 git cherry-pick
小明:没错!不过我的提交很多,还有没有其他方法呢?
————
同学C:别犹豫小伙!git rebase 欢迎你~
小明:对!就是它!

执行 git rebase master 之后,如图所示:

dev
master
小明 dev 复制版
创建新分支 dev
G
F
B
E
D
C
A
F1
G1

02 应用场景

2.1 合并子分支到主分支

$ git rebase <想要同步的分支名>

情景:你从主分支 master 切出分支 dev 进行开发。在你开发的过程中,你的小伙伴将他的修改早已合到 master 中,导致你的分支 dev 落后于 master ,这时想要同步 master 的改动。

操作说明👇👇👇

第 1 步,先查看下 master 和 dev 分支的提交记录:

# master 分支的提交( one 和 two 是小伙伴的提交)
$ git log --oneline
--->>
9ed2610 (HEAD -> master) calculate(two) # 2022-01-10 10:00
4803bd9 calculate(one)# 2022-01-05 17:00
819a8ce update 
69322b6 init
---<<

# dev 分支的提交 ( step 1/2/3/4 是自己的提交)
$ git log --oneline
--->>
a3154c7 (HEAD -> dev) step 4 # 2022-01-10 19:00
15135ef step 3 # 2022-01-06 10:00
3aad796 step 2 # 2022-01-04 16:00
a061fb6 step 1 # 2022-01-04 9:00
819a8ce update
69322b6 init
---<<

第 2 步,在 dev 分支里使用 rebase:

# 在 dev 分支里使用 rebase
$ git rebase master
--->>
Successfully rebased and updated refs/heads/dev.
---<<

第 3 步,再次查看 dev 分支里的提交,同步成功!!!

# 再次查看 dev 分支里的提交,确认是否同步成功
$ git log --oneline
--->>
4163d09 (HEAD -> dev) step 4 # 最新时间
6a48001 step 3 # 最新时间
4a1beb1 step 2 # 最新时间
fe0f3fc step 1 # 最新时间
9ed2610 (master) calculate-two # 2022-01-10 10:00
4803bd9 calculate-one # 2022-01-05 17:00
819a8ce update
69322b6 init
---<<

同步后提交记录示意图如下:

使用 rebase 同步 master 后的提交记录
step 4
step 3
step 2
step 1
calculate-two
calculate-one
update
init

试想:如果使用 git merge 最后打印出来的提交记录是什么样的呢?

首先,通过注释里的提交时间能够发现它们是交叉的,所以采用 git merge 的话,提交记录会不清晰,且若需要回滚涉及到合并节点也会很麻烦!

# 在 dev 分支里使用 merge
$ git merge master
 --->>
Merge made by the 'recursive' strategy.
 change.js | 3 +++
 hello.js  | 3 ++-
 2 files changed, 5 insertions(+), 1 deletion(-)
 ---<<
 
# 再次查看 dev 分支里的提交,确认是否同步成功
$ git log --oneline
--->>
99e4368 (HEAD -> dev) Merge branch 'master' into dev
a3154c7 step 4 # 2022-01-10 19:00
9ed2610 master calculate-two # 2022-01-10 10:00
15135ef step 3 # 2022-01-06 10:00
4803bd9 calculate-one # 2022-01-05 17:00
3aad796 step 2 # 2022-01-04 16:00
a061fb6 step 1 # 2022-01-04 9:00
819a8ce update
69322b6 init
---<<

同步后提交记录示意图如下:

使用 merge 同步 master 后的提交记录
step 4
calculate-two
step 3
calculate-one
step 2
step 1
update
init

2.2 将某段提交复制到另个分支上

$ git rebase <start_commitId> <end_commitId> --onto <branch_name>
# start_commitId 和 end_commitId 会指定的是一个前开后闭的区间
# 如果使用^ ,则包含 start_commitId,即 start_commitId^
# --onto 是将该指定的提交复制到 branch_name 分支上

情景:项目会存在多个分支,这时需要将某个分支中的某一大段提交应用到其他分支中。(若只有某一二三个提交,可以使用 cherry-pick 噢)

操作说明👇👇👇

第 1 步,先查看下 master 和 dev 分支的提交记录:

# dev 分支的提交
$ git log --oneline
--->>
4163d09 (HEAD -> dev) step 4 
6a48001 step 3
4a1beb1 step 2
fe0f3fc step 1
9ed2610 (master) calculate-two
4803bd9 calculate-one
819a8ce update
69322b6 init
---<<

# master 分支的提交
$ git log --oneline
--->>
9ed2610 (HEAD -> master) calculate(two)
4803bd9 calculate(one)
819a8ce update 
69322b6 init
---<<

现在想将 dev 中的 step 1 ~ step 3 的提交复制粘贴到 master 中

第 2 步,切换到 master 分支:

$ git checkout master

第 3 步,使用 rebase 命令进行复制粘贴:

$ git rebase fe0f3fc^ 6a48001
--->>                                         
Current branch 15135ef is up to date.
---<<

第 4 步,检查 master 记录,成功啦!

# master 分支的提交
$ git log --oneline   
--->>                                         
15135ef (HEAD -> master) step 3
3aad796 step 2
a061fb6 step 1
9ed2610 calculate(two)
4803bd9 calculate(one)
819a8ce update
69322b6 init
---<<

2.3 合并多个提交为一个提交

git rebase -i  <start_commitId> <end_commitId>
# -i(--interactive),参数含义是弹出交互式的界面让用户编辑完成合并操作
# start_commitId 和 end_commitId 会指定的是一个前开后闭的区间(使用^ 同理)
# 如果不指定 end_commitId,则该区间的终点默认是当前分支 HEAD 所指向的 commitId

情景:提交多次后,想让推送到远程仓库的提交记录简洁明了。

操作说明👇👇👇

第 1 步,先查看下 dev 分支的提交记录:

# dev 分支的提交
$ git log --oneline
--->>
a3154c7 (HEAD -> dev) step 4 
15135ef step 3
3aad796 step 2
a061fb6 step 1
819a8ce update
69322b6 init
---<<

现在将 step 1 ~ step 4 的提交合并为一个,且注释为 " add step all "

第 2 步,执行 rebase 命令:

$ git rebase -i a061fb6^ a3154c7
# -i 会唤起用户交互界面~

第 3 步,刚回车完 rebase 命令之后,会默认打开 vim,我们需要对未注释的行进行操作,即对本次 rebase 包含的所有提交进行操作,如下所示:

--->>
pick a061fb6 step 1
pick 3aad796 step 2
pick 15135ef step 3
pick a3154c7 step 4

# Rebase 5be4f27..a3154c7 onto 5be4f27 (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
---<<

补充知识👇
p / pick:保留该 commit
r / reword:保留该 commit,但要修改该 commit 的注释
e / edit:保留该 commit, 但要停下来修改该提交,不只是修改注释
s / squash:将该 commit 和前一个 commit 合并
f / fixup:将该 commit 和前一个 commit 合并,但不保留该提交的注释信息
x / exec:执行 shell 命令
d / drop:丢弃该 commit

第 4 步,修改后的信息如下,如下所示:

--->>
pick a061fb6 step 1
s 3aad796 step 2
s 15135ef step 3
s a3154c7 step 4
...(在此省略注释部分)
---<<

第 5 步,保存上一步修改的内容后,会自动到下一个交互阶段,如下:

--->>
# This is a combination of 4 commits.
# This is the 1st commit message:
step 1
# This is the commit message #2:
step 2
# This is the commit message #3:
step 3
# This is the commit message #4:
step 4
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Jan 24 20:40:38 2022 +0800
#
# interactive rebase in progress; onto 5be4f27
# Last commands done (4 commands done):
#    squash 15135ef step 3
#    squash a3154c7 step 4
---<<

第 6 步,修改后的信息如下:

--->>
...(在此省略注释部分)
add step all
...(在此省略注释部分)
---<<

第 7 步,保存后则会退出 vim 界面,展示下面的信息:

--->>
[detached HEAD 44c49f2] add step all
 Date: Mon Jan 24 20:40:38 2022 +0800
 2 files changed, 9 insertions(+), 1 deletion(-)
Successfully rebased and updated detached HEAD.
---<<

第 8 步,但此时咱们的这些修改还处于游离状态,可以简单的理解为只是复制粘贴到了 剪切板,还有真正的复制粘贴到 dev 分支中,所以需要先切换到 dev 分支~

$ git checkout dev
--->>
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  44c49f2 add step all

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 44c49f2

Switched to branch 'dev'
---<<
# 有人会疑惑,咱们一开始不就是在 dev 分支进行操作的吗,为啥还要切呢?
#答案是:在你进行 rebase 命令的时候,咱们相当于就游离在外啦,简单的说就是备份切换到了一个 临时分支(简单的理解就是 剪切板),所以想要复制粘贴到 dev 分支,就得先切回去啦~
#你看执行完 checkout,命令行就提醒我们需要去处理下 剪切板 里的信息呀

第 9 步,现在将 dev 分支的指针指向咱们最新的地方吧!(指针应该指向哪里呢?大家仔细看看上一步的 checkout 之后的提醒,有一个 commitId 在等着呢 👉 44c49f2)

$ git reset --hard 44c49f2
--->>
HEAD is now at 44c49f2 add step all
---<<

第 10 步,最后咱们看下 dev 分支的提交记录~成功!!!

$ git log --oneline
--->>             
44c49f2 (HEAD -> dev) add step all
819a8ce update
69322b6 init
---<<

2.4 rebase 过程中有冲突,如何解决

rebase 过程中发生代码冲突,可以采用以下配置项:

  • --continue :解决代码冲突后,需要将修改的文件加入暂存区 git add .,再执行 git rebase --continue,让 rebase 继续进行。

  • --abort:发生代码冲突后,放弃 rebase,回到操作前的样子。

  • --quit :发生代码冲突后,退出 rebase,但是不回到操作前的样子。

2.5 rebase 成功后,如何撤销

情景:从 master 分支拉取一个新的分支 dev,而后 master 新增两个提交 " change one " 和 " change two ",dev 分支新增一个提交 " change ",后来想让 dev 分支同步下 master 最新的改动就执行了 git rebase master ,现在希望能够撤销这次 rebase 操作。

第 1 步,执行 git reflog,查看所有操作日志:

$ git reflog
--->> 
3d399a5 HEAD@{0}: rebase (finish): returning to refs/heads/dev
3d399a5 HEAD@{1}: rebase (pick): change
74a3fd7 HEAD@{2}: rebase (start): checkout master
5a0c99c HEAD@{3}: commit: change
5be4f27 HEAD@{4}: checkout: moving from master to dev
---<<

第 2 步,从第 1 步打印出来的记录可以得到 rebase (start/pick/finish),而我们想要回到 rebase 之前的状态,就得回到 rebase (start) 之前,即回到节点 5a0c99c HEAD@{3}: commit: change

第 3 步,执行命令 git reset --hard 回到之前的状态:

# 方式 1 使用 commitId
git reset --hard 5a0c99c
# 方式 2 使用 HEAD@{n}
git reset --hard HEAD@{3}

要记得,最后需要使用 git log 打印提交记录来验证一下是否成功噢!

注意git reflog + git reset --hard 可以撤销任何命令,并回到想要的节点,所以请谨慎操作噢!ʕ •ᴥ•ʔ

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值