git rebase: 合并/截取commit 及 分支间的commit拼接
一. 只涉及单独分支的操作
1. 合并commit
当前分支状态:
A--------B-------C--------D
|--想要合并的commit--|
预计合并之后的状态:
A--------B'(包含C, D)
使用命令:
git rebase -i [start] [end]
start
和end
遵循左开右闭原则
1.-i
:启用交互,只有加了这个,才会有下面一步步的操作提示
2.[start]
: 如果想要合并B----C----D
,则[start]
为B
的前一个commit
, 即A
3.[end]
: 如果想要合并B----C----D
,则[end]
为D
; 如果缺省的话,默认为当前head
指向的commit
Note:[start]
和[end]
均为commit-ish
,有如下表示方法
----------------------------------------------------------------------
| Commit-ish | Examples
----------------------------------------------------------------------
| 1. <sha1> | dae86e1950b1277e545cee180551750029cfe735
| 2. <describeOutput> | v1.7.4.2-679-g3bee7fb
| 3. <refname> | master, heads/master, refs/heads/master
| 4. <refname>@{<date>} | master@{yesterday}, HEAD@{5 minutes ago}
| 5. <refname>@{<n>} | master@{1}
| 6. @{<n>} | @{1}
| 7. @{-<n>} | @{-1}
| 8. <refname>@{upstream} | master@{upstream}, @{u}
| 9. <rev>^ | HEAD^, v1.5.1^0
| 10. <rev>~<n> | master~3
| 11. <rev>^{<type>} | v0.99.8^{commit}
| 12. <rev>^{} | v0.99.8^{}
| 13. <rev>^{/<text>} | HEAD^{/fix nasty bug}
| 14. :/<text> | :/fix nasty bug
上表整理自StackOverFlow, 更多commit-ish理解可点这里
示例:
1. 当前状态:
2. 输入命令:
git rebase -i 869af45 9728be5
869af45
和9728be5
为hashID
的简写形式,git
会查找以其开头的hashID
此时会以编辑器(vim)形式进入第一个交互:
pick f9a2daa add 1.txt
pick dd0326f add 2.txt
pick 9728be5 add 3.txt
# Rebase 869af45..9728be5 onto 869af45 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
~
~
~
~
~
#
开头的都是帮助信息,真正要修改的是开头的三行
命令默认为pick
,即使用当前commit
, 因为我们想合并这三个commit
,所以将前三行修改为
pick f9a2daa add 1.txt
s dd0326f add 2.txt
s 9728be5 add 3.txt
表示采用第一个
commit
,并将后续的commit
合并到第一个commit.
然后用vim的保存操作保存后,若没有conflict
将再次以编辑器形式进入下一个交互:
Note: 若存在
conflict
, 则解决冲突后,输入git rebase --continue
继续操作,每次出现冲突,皆如此
# This is a combination of 3 commits.
# This is the 1st commit message:
add 1.txt
# This is the commit message #2:
add 2.txt
# This is the commit message #3:
add 3.txt
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Jul 25 16:57:09 2018 +0800
#
# interactive rebase in progress; onto 869af45
# Last commands done (3 commands done):
# s dd0326f add 2.txt
# s 9728be5 add 3.txt
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
# new file: 1.txt
# new file: 2.txt
# new file: 3.txt
#
# Untracked files:
# core/file.js
#
~
~
表明这三段commit
的message
将合并。如果要更改信息,可以在这里修改,不修改的话可以直接进行保存操作。这里我们直接保存,然后git
会做如下提示 :
我们发现操作成功了,但是当前的head
还处于游离状态,此时我们处于一个7fc1b94
的临时分支上,所以,接下来,将分支切换到之前的分支, 输入命令git reset --hard 7fc1b94
:
此时用git log
查看,会发现commit
已经如我们所愿地合并了。
2. 删除某段commit
当前分支状态:
A--------B-------C--------D-------E--------F
|--想要删除的commit--|
预计删除之后的状态:
A--------D'-------E'--------F'
示例:
还是用之前的示例:
1. 方法一:
在之前合并commit
的基础上,将如下
pick f9a2daa add 1.txt
s dd0326f add 2.txt
s 9728be5 add 3.txt
改为
pick f9a2daa add 1.txt
d dd0326f add 2.txt
s 9728be5 add 3.txt
d
即drop
, 弃用本行的commit
之后的操作和之前一样即可,然后我们发现:
PS: 当然如果你只想删除第二个
commit
而不想合并第一个和第二个,那么就写成
pick f9a2daa add 1.txt
d dd0326f add 2.txt
p 9728be5 add 3.txt
2. 方法二:
使用命令:
git rebase [start] [end] --onto <newbase>
[start]
,[end]
都和之前一样;
--onto <newbase>
表示为rebase
操作指定的基础点
以如下commit
树为例:
A--------B-------C--------D-------E--------F
|--想要删除的commit--|
分析如下:
(因为左开右闭原则,实际start为C)
--newbase--| start end
--------A--------B-------C--------D-------E--------F
|--想要删除的commit--|
示例:
仍然用之前的例子
则执行命令如下:
git rebase 5806b3 08676a5 --onto dff15b
PS:
1. 将--onto <newbase>
的位置调换这样写也是可以的:
git rebase --onto dff15b 5806b3 08676a5
2. 或者,你可以用其他commit-ish
表示法,例如:
git rebase 分支名~1 分支名 --onto 分支名~2
执行后,如果head
为游离态,则还是用老方法处理。操作成功后,我们可以看到:
二. 涉及多个分支间的操作
以下只提及操作命令,就不一一示例操作了(才不是因为我偷懒)
1. 拼接到头部
当前分支状态:
A--------B-------C--------D 分支A
\
E-------F--------G 分支B
|--想要拼接的commit--|
预计合并之后的状态:
A--------B-------C--------D 分支A
\
--------E'-------F'--------G' 分支B
可以直接在分支B上运行:
git rebase A B
或
git rebase A
2. 忽略中间的支线
当前分支状态:
A--------B-------C--------D 分支A
\
E-------F--------G 分支B
|-忽略的commit-| \
H-------I--------J 分支c
|--想要拼接的commit--|
预计合并之后的状态:
A--------B-------C--------D 分支A
\
--------H'-------I'--------J' 分支c
使用命令:
在分支C
上运行git rebase B C --onto A