1. 语法

1
2
3
4
5
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
[<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
git rebase --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch

1.1. 适用场景

1.1.1. 合并时精简提交历史

squash merge也可以精简提交历史,详情请见Git进阶--10、git-merge用法解析-用法大全

由于squash merge会变更提交者作者信息,这是一个很大的问题,后期问题追溯不好处理(当然也可以由分支dev的所有者来执行squash merge操作,以解决部分问题),rebase merge可以保留提交的作者信息,同时可以合并commit历史,完美的解决了上面的问题。

1.1.2. 合并完之后精简提交历史

如果从另一个分支中merge时,没有使用–squash选项时,导致合并过来好多没有必要的commit,这时就可以使用git rebase来调整commit的数量。

1.1.3. 更改提交历史

1.1.4. 防止git pull产生merge commit

  • 方法一: 在执行git pull的时候加上–rebase参数。这参数的意思就是在合并代码之前,先执行变基操作,成功后在进行真正的merge操作。(如果有冲突需要手动解决)
  • 方法二:在你的git bash里执行git config --global pull.rebase true。这个配置就是告诉git在每次pull前先进行rebase操作。这种方法和方法1原理一样,只不过方法1是每次pull前都要手动操作。

1.2. 使用方法

rebase之前


rebase merge分两步完成:

最终效果如1.2.3 最终效果所示,看起来dev分支是从M2拉出来的,而不是从B拉出来的。

1.2.1. 第一步执行rebase操作

可以使用-i参数手动调整commit历史,是否合并如何合并。例如下rebase -i命令会弹出文本编辑框:

1
2
git checkout dev
git rebase -i master
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
pick 6e17179 D1

f bb0bac0 D2

pick c9d8149 D3



# 变基 7ba16fe..c9d8149 到 7ba16fe(3 个提交)

#

# 命令:

# p, pick <提交> = 使用提交

# r, reword <提交> = 使用提交,但编辑提交说明

# e, edit <提交> = 使用提交,但停止以便在 shell 中修补提交

# s, squash <提交> = 使用提交,但挤压到前一个提交

# f, fixup [-C | -c] <提交> = 类似于 "squash",但只保留前一个提交

#                    的提交说明,除非使用了 -C 参数,此情况下则只

#                    保留本提交说明。使用 -c 和 -C 类似,但会打开

#                    编辑器修改提交说明

# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)

# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)

# d, drop <提交> = 删除提交

# l, label <label> = 为当前 HEAD 打上标记

# t, reset <label> = 重置 HEAD 到该标记

# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]

# .       创建一个合并提交,并使用原始的合并提交说明(如果没有指定

# .       原始提交,使用注释部分的 oneline 作为提交说明)。使用

# .       -c <提交> 可以编辑提交说明。

#

# 可以对这些行重新排序,将从上至下执行。

#

# 如果您在这里删除一行,对应的提交将会丢失。

#

# 然而,如果您删除全部内容,变基操作将会终止。

#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜  mergedemo git:(dev) git rebase -i master 

自动合并 file.txt

冲突(内容):合并冲突于 file.txt

error: 不能应用 47f4c7f... D1

提示: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".

不能应用 47f4c7f... D1

合并冲突后,执行git add .不需要执行git commit ,然后执行git rebase --continue,继续交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
D1'



# 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交

# 说明将会终止提交。

#

# 交互式变基操作正在进行中;至 d48f03a

# 最后完成的命令(1 条命令被执行):

#    pick 47f4c7f D1

# 接下来要执行的命令(剩余 2 条命令):

#    fixup 37d76b0 D2

#    pick b9639e3 D3

# 您在执行将分支 'dev' 变基到 'd48f03a' 的操作。

#

# 要提交的变更:

#       修改:     file.txt

#
1
2
3
4
5
6
7
➜  mergedemo git:(42c38aa) ✗ git rebase --continue

[分离头指针 ab44b6f] D1'

 1 file changed, 1 insertion(+)

成功变基并更新 refs/heads/dev。
1
2
3
4
5
6
7
8
9
10
11
12
13
cf8116c (HEAD -> dev) D3

39784fd D1'

3ddbd5d (master) M2

5fb7fba M1

daf563a B

edfa896 A

(END)

1.2.2. 第二步执行merge操作

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  mergedemo git:(dev) git checkout master  

切换到分支 'master'

➜  mergedemo git:(master) git merge dev      

更新 42c38aa..bec7048

Fast-forward

 file.txt | 3 +++

 1 file changed, 3 insertions(+)

1
2
3
4
5
6
7
8
9
10
11
12
13
bec7048 (HEAD -> master, dev) D3

5243e08 D1'

42c38aa M2

7132c18 M1

1de9541 B

37f978a A

(END)

1.2.3. 最终效果

原图请见:https://www.processon.com/view/6325a4f01efad46b0ab39c8d?fromnew=1

1.2.4. 交互式commands

  • 修改提交信息

  • 合并多个提交

  • 拆分、编辑已有提交

  • 对提交重排序

  • 删除提交

  • p, pick = 合并这次提交

  • r, reword = 合并这次提交, 但是需要修改此次提交的日志信息

  • e, edit = 合并这次提交, 但需要对这次提交进行内容和日志的修改

  • s, squash =合并这次提交,但把这次提交和上一次提交融合在一起提交,会让提交者重新编辑提交日志。squash这个词用的好,把多个提交压扁在一起。

  • f, fixup = 和squash基本一样,差别在于直接丢弃此次提交的日志,使用上一次提交的日志,所以没有交互过程。

  • x, exec = 不合并这次提交,而是执行后面的shell命令。

[[../../../../cubox/006-ChromeCapture/20220917-Git Rebase交互式合并详解 Walker’s Blog]]

2. 其他妙用 onto

使用onto之后, 后面会跟3个参数,可以更精确地变基
$ git rebase –onto base from to
命令的意义使用(from, to]所指定的范围内的所有commit在base这个commit之上进行重建,相当于找到from和to公共节点之后,to分支所做的内容,在base上重演.

2.1. 踢除分支上临时功能提交

1
2
3
4
5
6
7
8
1.当前工作空间---分支A
2.从当前工作的分支新建一个分支,并且换到该分支----git checkout -b newbranch
3.git rebase --onto B 开始的commitId 结束的commitId
4.生成一个基于B分支和选择的提交区间的片段生成一个新的分支(detached Head)
5.从当前工作空间新建切换到一个新的分支---git checkout -b branch_bank
6.分支branch_bank的代码就是我们所需要的了
7.branch_bank强制覆盖A分支,切换到A分支,---git reset --hard origin branch_bank
8.代码就恢复正常啦

2.2. 跳过某分支合并

我们可以通过 rebase 对两个分支进行对比,取出相应的修改,然后应用到另一个分支上。例如:

1
2
3
4
5
    F---G patch
/
D---E feature
/
A---B---C master

假设我们基于 feature 分支的提交 D 创建了分支 patch,并且新增了提交 F、G,现在我们想将 patch 所做的更改合并到 master 并发布,但暂时还不想合并 feature ,这种情况下可以使用 rebase 的 --onto <branch> 选项:

1
git rebase --onto master feature patch

以上操作将取出 patch 分支,对比它基于 feature 所做的更改, 然后把这些更改在 master 分支上重新应用,让 patch 看起来就像直接基于 master 进行更改一样。执行后的 patch 如下:

1
A---B---C---F'---G' patch

然后我们可以切换到 master 分支,并对 patch 执行快进合并:

1
2
git checkout master
git merge patch

2.2.1. 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cd ~

rm -rf mergedemo

mkdir mergedemo;cd mergedemo

git init .

echo "hello A" > file.txt

git add .

git commit -m "A"

echo "hello B" >> file.txt

git add .

git commit -m "B"


echo "hello C" >> file.txt

git add .

git commit -m "C"

git log --oneline --all --graph
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
git checkout -b feature

git reset --hard HEAD~2

echo "hello D" > file.txt

git add .

git commit -m "D"

echo "hello E" >> file.txt

git add .

git commit -m "E"

git log --oneline --all --graph
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
git checkout -b patch

git reset --hard HEAD^

echo "hello F" >> file.txt

git add .

git commit -m "F"

echo "hello G" >> file.txt

git add .

git commit -m "G"

git log --oneline --all --graph
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
* b3bdd3a (HEAD -> patch) G

* d6cbc3d F

| * 8a5a826 (feature) E

|/  

* fe63584 D

| * ed6ecfe (master) C

| * 76f5d76 B

|/  

* 0d7ae83 A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜  mergedemo git:(patch) git rebase --onto master feature patch
自动合并 file.txt

冲突(内容):合并冲突于 file.txt

error: 不能应用 c5872ed... F

提示: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".

不能应用 c5872ed... F

➜  mergedemo git:(3877ba7) ✗ vim file.txt

➜  mergedemo git:(3877ba7) ✗ git add .                             

➜  mergedemo git:(3877ba7) ✗ git rebase --continue                 

[分离头指针 864b843] F

 1 file changed, 2 insertions(+)

成功变基并更新 refs/heads/patch。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
* e8f974c (HEAD -> patch) G

* 864b843 F

* 3877ba7 (master) C

* 0d80606 B

| * 4a6d01f (feature) E

| * a9019fa D

|/  

* 34bfc52 A
1
2
3
4
5
6
7
8
9
10
11
12
13
➜  mergedemo git:(patch) git checkout master            

切换到分支 'master'

➜  mergedemo git:(master) git merge patch    

更新 3877ba7..e8f974c

Fast-forward

 file.txt | 3 +++

 1 file changed, 3 insertions(+)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
* e8f974c (HEAD -> master, patch) G

* 864b843 F

* 3877ba7 C

* 0d80606 B

| * 4a6d01f (feature) E

| * a9019fa D

|/  

* 34bfc52 A

(END)

3. 参考

http://walkerdu.com/2019/12/05/git_rebase/
https://www.qikegu.com/docs/4399
https://juejin.cn/post/7093416208296312846#heading-0
https://morningspace.github.io/tech/git-merge-stories-7/
https://waynerv.com/posts/git-rebase-intro/
https://juejin.cn/post/6844903762415337479
https://wikinote.gitbook.io/git-learning/git-ji-ben-ming-ling/fen-zhi-cao-zuo-yu-guan-li/git-rebase