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