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

本文详细介绍了如何使用git rebase进行commit合并与删除,包括在单一分支上的操作和涉及多个分支的复杂操作,如合并commit、删除某段commit、分支间commit的拼接,以及忽略中间的支线。
875

被折叠的 条评论
为什么被折叠?



