Git简明手册——Git rebase使用

reabse介绍

我们在使用git提交远程仓库,或者同步远程仓库代码到本地仓库,查看提交记录的时候,提交历史往往是一条分叉的树,这在多人协作开发的过程中是十分常见的。有没有办法使我们的提交干净整洁?提交记录更加简洁一点?这时候rebase派上用场。git rebase 有三个作用:1,合并多个commmit为一个完整的commit;2,将提交记录整洁干净;3,将某一段commit合并到另一个分支。接下来将详细介绍这三个作用的使用场景和用法。

rebase作用

合并多个commit为一个完整的commit

1.首先查看工作区状态,确保工作区、暂存区、版本库已经远程仓库一致。然后我们做一些普通的addcommit 操作。

1
D:\rushgit (master -> origin)
2
λ git status      ##查看当前状态
3
On branch master
4
nothing to commit, working directory clean
5
D:\rushgit (master -> origin)
6
λ touch a.go      ##创建一个新文件a.go,添加到暂存区并提交到本地版本库
7
D:\rushgit (master -> origin)
8
λ git status
9
On branch master
10
Untracked files:
11
  (use "git add <file>..." to include in what will be committed)
12
        a.go
13
nothing added to commit but untracked files present (use "git add" to track)
14
D:\rushgit (master -> origin)
15
λ git add a.go
16
D:\rushgit (master -> origin)
17
λ git commit -m 'add-a.go'
18
[master ed0e588] 'add-a.go'
19
 1 file changed, 0 insertions(+), 0 deletions(-)
20
 create mode 100644 a.go
21
D:\rushgit (master -> origin)
22
λ touch b.go    ##创建一个新文件b.go,添加到暂存区并提交到本地版本库
23
D:\rushgit (master -> origin)
24
λ git add b.go
25
D:\rushgit (master -> origin)
26
λ git commit -m 'add-b.go'
27
[master 2c0c671] 'add-b.go'
28
 1 file changed, 0 insertions(+), 0 deletions(-)
29
 create mode 100644 b.go
30
D:\rushgit (master -> origin)
31
λ vim a.go    ##修改a.go文件的内容,添加到暂存区并提交到本地版本库
32
D:\rushgit (master -> origin)
33
λ git status
34
On branch master
35
Changes not staged for commit:
36
  (use "git add <file>..." to update what will be committed)
37
  (use "git checkout -- <file>..." to discard changes in working directory)
38
        modified:   a.go
39
no changes added to commit (use "git add" and/or "git commit -a")
40
D:\rushgit (master -> origin)
41
λ git add a.go
42
D:\rushgit (master -> origin)
43
λ git commit -m 'modify-a.go'

这时候图形化方式查看一下git的提交历史记录:

1
λ git log --graph --pretty=oneline
2
*409c288f44bcbcbcb3f2a9a91ac00b7967a2ec91 'modify-a.go'
3
*2c0c671f35f238a1367eea8681bcdcad82b4ba1d 'add-b.go'
4
*ed0e58875017a20e4c165f5e6746fe939a6f7ac7 'add-a.go'

可以看到本地版本库有三条提交记录,分别对应我们的三次提交。这三次的更改就是添加了a.go,b.go,并修改了a.go。当我们向远程关联分支提交的时候,可不可以合并成一个commit呢?就是通过rebase功能。

2.使用git rebase 合并多个commit为一个commit,使本地版本仓库的提交记录简洁,提交到远程也更清晰。
命令为git rebase -i [start commit-id] [end commit-id]。start commit-id和end commit-id按照字面意思就是开始id和结束id,也就是你想合并哪一段区间的提交。这个区间是前开后闭的,也就是如果你想合并3个commit的提交,那么start commit-id需要设置为倒数第四个commit-id。此处我们应该设置为bdc2c7a88

1
λ git rebase -i bdc2c7a88
2
pick ed0e588 'add-a.go'
3
pick 2c0c671 'add-b.go'
4
pick 409c288 'modify-a.go'
5
# Rebase bdc2c7a..409c288 onto bdc2c7a (3 command(s))
6
#
7
# Commands:
8
# p, pick = use commit
9
# r, reword = use commit, but edit the commit message
10
# e, edit = use commit, but stop for amending
11
# s, squash = use commit, but meld into previous commit
12
# f, fixup = like "squash", but discard this commits log message
13
# x, exec = run command (the rest of the line) using shell
14
# d, drop = remove commit
15
#
16
# These lines can be re-ordered; they are executed from top to bottom.
17
#
18
# If you remove a line here THAT COMMIT WILL BE LOST.
19
#
20
# However, if you remove everything, the rebase will be aborted.
21
#
22
# Note that empty commits are commented out

此时可以看到git给出了此次rebase的操作提示,在此给一下相关命令的解释:

1
1,p(pick)保留此commit
2
2,r(reword)保留此commit但是需要修改commit的注释
3
3,e(edit)保留该commit, 但要停下来修改该提交(不仅仅修改注释)
4
4,s(squash)将该commit和前一个commit合并
5
5,f(fixup):将该commit和前一个commit合并,但不要保留该提交的注释信息
6
6,e(exec):执行shell命令
7
7,d(drop):要丢弃该commit(缩写:d)

此时如果我们只保留最后一个commit(409c28),其他两个commit直接和前一个commit合并,但是同时保留注释信息。这时候就修改命令行如下:

1
p ed0e588 'add-a.go'
2
s 2c0c671 'add-b.go'
3
s 409c288 'modify-a.go'     ##此时出现如下界面,提示我们即将完成的操作,可以继续修改commit相关信息。  
4
###################################################
5
# This is a combination of 3 commits.
6
# The first commits message is:
7
'add-a.go'
8
# This is the 2nd commit message:
9
'add-b.go'
10
# This is the 3rd commit message:
11
'modify-a.go'
12
# Please enter the commit message for your changes. Lines starting
13
# with '#' will be ignored, and an empty message aborts the commit.
14
#
15
# Date:      Sat May 11 22:58:50 2019 +0800
16
#
17
# interactive rebase in progress; onto bdc2c7a
18
# Last commands done (3 commands done):
19
#    s 2c0c671 'add-b.go'
20
#    s 409c288 'modify-a.go'
21
# No commands remaining.
22
# You are currently editing a commit while rebasing branch 'master' on 'bdc2c7a'.
23
#
24
# Changes to be committed:
25
#       new file:   a.go
26
#       new file:   b.go
27
#
28
```  
29
3.此时可以看到rebase的效果如下:
30
![git-rebase结果](Git手册(五)/git-result.PNG)  
31
使用git log查看结果如下(reabase操作会生成一个新的commit-id来完成合并):  
32
```java
33
λ git log
34
commit cf0a58d24cbf09973645baaab039177bd857328d
35
Author: VfEver <13146770925@126.com>
36
Date:   Sat May 11 22:58:50 2019 +0800
37
    'add-a.go'
38
    'add-b.go'
39
    'modify-a.go'

简洁的提交记录,去除分叉

1.从当前分支新拉出来一个分支,取名为develop,然后从此分支做一个commit,然后在master分支做一个commit并提交。此时develop分支和远程master分支就有了分叉。

1
D:\rushgit (master -> origin)
2
λ git status
3
On branch master
4
nothing to commit, working directory clean
5
D:\rushgit (master -> origin)
6
λ git checkout -b develop
7
Switched to a new branch 'develop'
8
D:\rushgit (develop -> origin)
9
λ touch c.go
10
D:\rushgit (develop -> origin)
11
λ git add c.go
12
D:\rushgit (develop -> origin)
13
λ git commit -m 'add-g.go'
14
[develop 861b393] 'add-g.go'
15
 1 file changed, 0 insertions(+), 0 deletions(-)
16
 create mode 100644 c.go
17
 D:\rushgit (master -> origin)
18
λ git checkout master  ##在此做一个add、commit、push(add commit省略)
19
 Switched to branch 'master'
20
λ git push
21
Counting objects: 6, done.
22
Delta compression using up to 4 threads.
23
Compressing objects: 100% (4/4), done.
24
Writing objects: 100% (6/6), 599 bytes | 0 bytes/s, done.
25
Total 6 (delta 1), reused 0 (delta 0)
26
remote: Resolving deltas: 100% (1/1), done.
27
To git@github.com:VfEver/rushgit.git
28
   bdc2c7a..fc6eb4b  master -> master
29
```  
30
![git-rebase](Git手册(五)/git_rebase_1.PNG)  
31
2.此时如果我们在develop分支pull一下,将远程分支的提交同步到本地develop分支:
32
```java
33
D:\rushgit (develop -> origin)
34
λ git pull origin master
35
From github.com:VfEver/rushgit
36
 * branch            master     -> FETCH_HEAD
37
Merge made by the 'recursive' strategy.
38
 e.go | 0
39
 1 file changed, 0 insertions(+), 0 deletions(-)
40
 create mode 100644 e.go

此时的pull操作还会产生一个commit-id,现在的分支树用图形表示如下:
git-rebase
可以看到develop和远程master合并之后,产生了一个新的commit-id(ca8fe16),同时合并了远程的仓库提交历史,并产生了分叉。此时本地develop分支结果如下:
git-rebase
3.如何解决这个分叉?就是接下来rebase的操作,我们先将本地develop分支回滚到上一个版本。

1
D:\rushgit (develop -> origin)
2
λ git reset --hard 861b3
3
HEAD is now at 861b393 'add-g.go'
4
D:\rushgit (develop -> origin)
5
λ git log
6
commit 861b39322ed33744365bd322b8dcd26c7d5d859c
7
Author: VfEver <13146770925@126.com>
8
Date:   Sun May 12 09:25:23 2019 +0800
9
    'add-g.go'
10
commit cf0a58d24cbf09973645baaab039177bd857328d

此时我们进行rebase操作,将master分支合并到本地develop分支:

1
λ git rebase fc6eb4b1
2
First, rewinding head to replay your work on top of it...
3
Applying: 'add-g.go'
4
λ git log --pretty=oneline
5
f5c7f65eacd5f7f93747698d436f8c764ec4e916 'add-g.go'
6
fc6eb4b1ca7530aa39fcd3a3872317b7d715e703 'add-e.go'
7
cf0a58d24cbf09973645baaab039177bd857328d 'add-a.go'
8
bdc2c7a88bbd7d4b4fa41bc0b61576b79f8e8efa 'modify-lww.txt'
9
7f985260c0b40d9f6db8f251bc067abf69121f37 'add'
10
f5da69a6bdf6a44d75f7b4088b2d26d59abe497f add go.txt
11
9ffb52623011006575566736bc0c387107dae5c6 Merge branch 'master' of github.com:VfEver/rushgit
12
3125251a15943118724f9dce6302c7351444880e Update README.md
13
9e7674cbd635f8e52e2434113e05479bf7281b9d Initial commit
14
68c86da5dc6372755fe7048847aed6cd9b8f001f add java9

此时的develop分支提交树则变成了如下(一条干净整洁的直线):
git-rebase

将某一段commit合并到另一个分支

git-rebase
如图我想将处于develop分支的C5到C6的提交粘贴至master分支,这个时候通过rebase功能也可以达到这个效果。
1.checkout develop分支,在develop分支做三次commit(C4、C5、C6)。之后在master分支做一个C3的commit。

1
D:\rushgit (develop -> origin)
2
λ touch z.sh
3
D:\rushgit (develop -> origin)
4
λ git add z.sh
5
D:\rushgit (develop -> origin)
6
λ git commit -m 'add-z.sh'
7
[develop 16c1602] 'add-z.sh'
8
 1 file changed, 0 insertions(+), 0 deletions(-)
9
 create mode 100644 z.sh
10
D:\rushgit (develop -> origin)
11
λ touch x.sh
12
D:\rushgit (develop -> origin)
13
λ git add x.sh
14
D:\rushgit (develop -> origin)
15
λ git commit -m 'add-x.sh'
16
[develop 298bf3a] 'add-x.sh'
17
 1 file changed, 0 insertions(+), 0 deletions(-)
18
 create mode 100644 x.sh
19
D:\rushgit (develop -> origin)
20
λ touch c.sh
21
D:\rushgit (develop -> origin)
22
λ git add c.sh
23
D:\rushgit (develop -> origin)
24
λ git commit -m 'add-c.sh'
25
[develop 5d2f6c0] 'add-c.sh'
26
 1 file changed, 0 insertions(+), 0 deletions(-)
27
 create mode 100644 c.sh
28
D:\rushgit (develop -> origin)
29
λ git checkout master
30
Switched to branch 'master'
31
Your branch is up-to-date with 'origin/master'.
32
D:\rushgit (master -> origin)
33
λ touch v.sh
34
D:\rushgit (master -> origin)
35
λ git add v.sh
36
D:\rushgit (master -> origin)
37
λ git commit -m 'add-v.sh'
38
[master 7978153] 'add-v.sh'
39
 1 file changed, 0 insertions(+), 0 deletions(-)
40
 create mode 100644 v.sh

2.此时在master分支,将develop分支的C5-C6的提交粘贴至当前分支,就是使用git rebase [start commit-id] [end commit-id] –onto [branchName] 。同上,这个起止点也是一个左开右闭区间,所以起点要前进一个,也就是git rebase C4 C6 –onto master。

1
D:\rushgit (develop -> origin)
2
λ git rebase 16c1602 5d2f6c --onto master
3
First, rewinding head to replay your work on top of it...
4
Applying: 'add-x.sh'
5
Applying: 'add-c.sh'
6
D:\rushgit (HEAD detached at 4ebf303 -> origin)

此时HEAD指向了4ebf303(当前的HEAD可以认为是一个游离的HEAD),一个新的commit-id。此时master分支HEAD其实并没有指向当前粘贴过来的现在的commit-id。当我们试图切回master分支的时候,会给我们一个提示:

1
λ git checkout master
2
Warning: you are leaving 2 commits behind, not connected to
3
any of your branches:
4
5
  4ebf303 'add-c.sh'
6
  57f109c 'add-x.sh'
7
If you want to keep them by creating a new branch, this may be a good time
8
to do so with:
9
 git branch <new-branch-name> 4ebf303
10
Switched to branch 'master'
11
Your branch is ahead of 'origin/master' by 1 commit.
12
  (use "git push" to publish your local commits)

提示显示,此时正在丢弃两个提交,而且这两个提交并没有关联到任何分支。我们也可以通过命令git branch 4ebf303通过新建一个分支完成关联。此时切回master分支,也可以通过git reset –hard 4ebf303完成master分支的HEAD指向4ebf303。

1
D:\rushgit (master -> origin)
2
λ git reset --hard  797815
3
HEAD is now at 7978153 'add-v.sh'
4
λ git log --pretty=oneline
5
4ebf303885c1c4e3a034b4b3fd2ad8ff0debff74 'add-c.sh'
6
57f109c8ea5805bfdaee7631eb2575ff71317a49 'add-x.sh'
7
79781531517c9f834564f9a43ef213e90cc74156 'add-v.sh'
8
584b02b6bc55a76b50f83a466f505ee08ad6d189 'add-w.go'
9
fc6eb4b1ca7530aa39fcd3a3872317b7d715e703 'add-e.go'
10
cf0a58d24cbf09973645baaab039177bd857328d 'add-a.go'
11
bdc2c7a88bbd7d4b4fa41bc0b61576b79f8e8efa 'modify-lww.txt'
12
7f985260c0b40d9f6db8f251bc067abf69121f37 'add'
13
f5da69a6bdf6a44d75f7b4088b2d26d59abe497f add go.txt
14
9ffb52623011006575566736bc0c387107dae5c6 Merge branch 'master' of github.com:VfEver/rushgit
15
3125251a15943118724f9dce6302c7351444880e Update README.md
16
9e7674cbd635f8e52e2434113e05479bf7281b9d Initial commit
17
68c86da5dc6372755fe7048847aed6cd9b8f001f add java9

这就完成了C5、C6到master分支C3后的粘贴。如果使用简单的git rebase [branch-name],就是单纯将branch-name的分支上提交的代码复制到当前分支,当前的分支提交在后(也就是作为最新的commit添加在从其他分支粘贴过来的提交之后)。

参考链接

  1. https://www.codercto.com/a/43072.html
  2. http://gitbook.liuhui998.com/4_2.html
  3. https://www.jianshu.com/p/4a8f4af4e803
坚持原创分享