git-rebase進階使用

xiaojiangkf發表於2019-07-30

git rebase進階使用

推薦書籍: 《git pro》

推薦教程: atlassian

本地獲取幫助: git help <command>

  • rebase最強大的地方在於可以按需移動提交併對其進行編輯更改,當然前提是在個人分支

  • 最好對git中的一些概念預先有一定的瞭解

  • 再次強調:git rebase相關命令只允許在個人分支上使用

目錄:

  • 本文alias說明
  • 用法一:將當前分支的提交變基到目標分支
  • 用法二:將當前分支變基到之前的提交, 重寫其後的提交
  • 用法三:將當前分支某一段提交變基到目標分支
  • 用法四:變基時保留合併提交
  • 衝突

alias說明

  • git ll = git log --oneline
  • git br = git branch
  • git st = git status
  • git ci = git commit
  • git lh = log --pretty=format:'%Cred%h %C(yellow)%ad%Creset %Cred%s%Creset %Cblue[%an] %C(yellow)%d' --graph --date=short --topo-order

用法一

將當前分支的提交變基到目標分支: git rebase <upstream> [<branch>]

  • <upstream>:目標分支或新基點

  • 如果提供<branch>,則會執行git checkout <branch>,預設為HEAD

01

02

>git br
    * master
      test
>git ll master
    1a17ba1 (HEAD -> master) add c
    4428c06 add a
>git ll test
    d691c94 (test) add b
    4428c06 (HEAD -> master) add a
>git co test
>git rebase master (或 git rebase master test)
    First, rewinding head to replay your work on top of it...
    Applying: add b
>git br
      master
    * test
>git ll test
    30cec3f (HEAD -> test) add b
    1a17ba1 (master) add c
    4428c06 add a
>git ll master
    1a17ba1 (master) add c
    4428c06 add a

可能的場景:

  • 在將當前分支合併到主分支前執行變基操作,然後再合併到主分支,可以實現fast-forward

用法二

將當前分支變基到之前的某一提交, 重寫其後的提交:git rebase -i <upstream> [<branch>]

03
在這裡插入圖片描述

>git ll test
    4a4633c (HEAD -> test) change d
    f3ff56f add d
    30cec3f add b
    1a17ba1 (master) add c
    4428c06 add a
> git rebase -i HEAD~2
    >>pick f3ff56f add d
    >>pick 4a4633c change d
    //改為並儲存:
    >>pick f3ff56f add d
    >>suqash 4a4633c change d
    //輸入提交資訊並儲存:
    >>add and change d
    [detached HEAD fb38e3f] add and change d
     Date: Mon Jul 22 19:46:25 2019 +0800
     1 file changed, 2 insertions(+)
     create mode 100644 d
    Successfully rebased and updated refs/heads/test.
>git ll test
    fb38e3f (HEAD -> test) add and change d
    30cec3f add b
    1a17ba1 (master) add c
    4428c06 add a

互動式rebase的選項說明:

pick f3ff56f add d
pick 4a4633c change d

# Rebase 30cec3f..4a4633c onto 30cec3f (2 commands) //表示將30cec3f..4a4633c(左開右閉)變基到30cec3f,包含2條命令,即有兩個提交需要處理
#
# Commands:
# p, pick = use commit //表示正常使用該提交
# r, reword = use commit, but edit the commit message //正常使用該提交,但會提示修改提交資訊
# e, edit = use commit, but stop for amending //正常使用該提交內容,但提交後會停下,此時可以執行git commit --amend或者其他命令,最後git rebase --continue
# s, squash = use commit, but meld into previous commit //與前一提交合並,會提示輸入提交資訊
# f, fixup = like "squash", but discard this commit's log message //與上一提交合並,但不會使用該提交資訊 類似於git commit --amend --no-edit
# 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 //會忽略由merge生成的提交

可能的場景:

  • 可以在自己本地分支上將多個commit合併為一個:squash
  • 可以在自己本地分支上重新規劃提交順序
  • 可以在自己本地分支上重寫某提交註釋資訊:reword,類似git commit --amend的功能
  • 可以在自己本地分支上移除某提交:drop或直接刪除該行
  • 可以在自己本地分支上移除空merge commit
D:\tmp\gittest\git1>git lh
* 44dd22a 2019-07-24 add f [userName]  (HEAD -> master)
*   0141788 2019-07-24 Merge branch 'test' [userName] //空提交
|\
| * e52ab99 2019-07-24 add e [userName]  (test)
* | ea9bded 2019-07-24 change d [userName]
* | 7656327 2019-07-24 add d [userName]
* | 4940b1f 2019-07-24 add c [userName]
|/
* f41fe7f 2019-07-24 add b [userName]
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

D:\tmp\gittest\git1>git rebase -i 4940b1f
Successfully rebased and updated refs/heads/master.

D:\tmp\gittest\git1>git lh
* 02e856a 2019-07-24 add f [userName]  (HEAD -> master)
* a94b7a5 2019-07-24 add e [userName]
* ea9bded 2019-07-24 change d [userName]
* 7656327 2019-07-24 add d [userName]
* 4940b1f 2019-07-24 add c [userName]
* f41fe7f 2019-07-24 add b [userName]
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

如果merge commit不是空提交,則要手動解決衝突:

D:\tmp\gittest\git1>git lh
* 8a5ce3e 2019-07-24 add e [userName]  (HEAD -> master)
*   f037d3b 2019-07-24 Merge branch 'fix' Conflicts:f [userName] //合併時解決衝突
|\
| * ec2312c 2019-07-24 change f fix [userName]  (fix)
* | e3a88f2 2019-07-24 change f master [userName]
|/
* 02e856a 2019-07-24 add f [userName]
* a94b7a5 2019-07-24 add e [userName]
* ea9bded 2019-07-24 change d [userName]
* 7656327 2019-07-24 add d [userName]
* 4940b1f 2019-07-24 add c [userName]
* f41fe7f 2019-07-24 add b [userName]
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

D:\tmp\gittest\git1>git rebase -i e3a88f2
error: could not apply ec2312c... change f fix

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Could not apply ec2312c... change f fix
Auto-merging f
CONFLICT (content): Merge conflict in f

D:\tmp\gittest\git1>git st
interactive rebase in progress; onto e3a88f2
Last command done (1 command done):
   pick ec2312c change f fix
Next command to do (1 remaining command):
   pick 8a5ce3e add e
  (use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'master' on 'e3a88f2'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   f

no changes added to commit (use "git add" and/or "git commit -a")

D:\tmp\gittest\git1>gvim f

D:\tmp\gittest\git1>git add f

D:\tmp\gittest\git1>git ci
[detached HEAD e531346] change f fix Conflicts:f2
 1 file changed, 1 insertion(+)

D:\tmp\gittest\git1>git st
interactive rebase in progress; onto e3a88f2
Last command done (1 command done):
   pick ec2312c change f fix
Next command to do (1 remaining command):
   pick 8a5ce3e add e
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on 'e3a88f2'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

nothing to commit, working tree clean

D:\tmp\gittest\git1>git rebase --continue
Successfully rebased and updated refs/heads/master.

D:\tmp\gittest\git1>git lh
* c5516c4 2019-07-24 add e [userName]  (HEAD -> master)
* e531346 2019-07-24 change f fix Conflicts:f2 [userName]
* e3a88f2 2019-07-24 change f master [userName]
* 02e856a 2019-07-24 add f [userName]
* a94b7a5 2019-07-24 add e [userName]
* ea9bded 2019-07-24 change d [userName]
* 7656327 2019-07-24 add d [userName]
* 4940b1f 2019-07-24 add c [userName]
* f41fe7f 2019-07-24 add b [userName]
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

  • 可以在自己本地分支上拆分commit:互動式rebase時選擇edit,之後可以對該commit進行操作
D:\tmp\gittest\git1>git br
* master

D:\tmp\gittest\git1>git lh
* 645a536 2019-07-24 add d [userName]  (HEAD -> master)
* 94f1b39 2019-07-24 add b c [userName] //準備拆分該提交
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

D:\tmp\gittest\git1>git rebase -i HEAD~2

    pick 94f1b39 add b c
    pick 645a536 add d
    改為
    edit 94f1b39 add b c
    pick 645a536 add d

Stopped at 94f1b39...  add b c
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

D:\tmp\gittest\git1>git reset HEAD~1 //撤銷提交

D:\tmp\gittest\git1>ls
a  b  c

D:\tmp\gittest\git1>git st
interactive rebase in progress; onto 1a32cb0
Last command done (1 command done):
   edit 94f1b39 add b c
Next command to do (1 remaining command):
   pick 645a536 add d
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '1a32cb0'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        b
        c

nothing added to commit but untracked files present (use "git add" to track)

D:\tmp\gittest\git1>git add b //拆解提交1

D:\tmp\gittest\git1>git ci -m "add b"
[detached HEAD f41fe7f] add b
 1 file changed, 2 insertions(+)
 create mode 100644 b

D:\tmp\gittest\git1>git add c //拆解提交2

D:\tmp\gittest\git1>git ci -m "add c"
[detached HEAD 4940b1f] add c
 1 file changed, 2 insertions(+)
 create mode 100644 c

D:\tmp\gittest\git1>git rebase --continue
Successfully rebased and updated refs/heads/master.

D:\tmp\gittest\git1>git lh
* 7656327 2019-07-24 add d [userName]  (HEAD -> master)
* 4940b1f 2019-07-24 add c [userName]
* f41fe7f 2019-07-24 add b [userName]
* 1a32cb0 2019-07-23 change a [userName]
* 1fc8279 2019-07-23 add a [userName]

  • 可以在自己本地分支上同時完成以上調整

用法三

將當前分支某一段提交變基到目標分支:git rebase --onto <newBase> <ref-start> <ref-end>

  • 因為提供了<ref-end>這個引數,預設會在後臺執行git checkout <branch>命令,如果沒有分支指向它,則會處於detached狀態。
  • --onto 選項相當於執行了git reset --hard <newBase>
D:\tmp\gittest\master>git ll test
fb38e3f (HEAD -> test) add and change d
30cec3f add b
1a17ba1 (master) add c
4428c06 add a

D:\tmp\gittest\master>git ll master
1a17ba1 (master) add c
4428c06 add a

D:\tmp\gittest\master>git co master
Switched to branch 'master'

D:\tmp\gittest\master>git merge test
Updating 1a17ba1..fb38e3f
Fast-forward
 b | 1 +
 d | 2 ++
 2 files changed, 3 insertions(+)
 create mode 100644 b
 create mode 100644 d

D:\tmp\gittest\master>git ll master
fb38e3f (HEAD -> master, test) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

04

D:\tmp\gittest\master>git ll test
40dcfec (HEAD -> test) change g
dc0ddd2 add g
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

D:\tmp\gittest\master>git ll dev
87c6bae (dev) change e
8d7cfc9 add e
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

D:\tmp\gittest\master>git ll fix
a355be9 (fix) change f
0e24647 add f
87c6bae (dev) change e
8d7cfc9 add e
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

12

D:\tmp\gittest\master>git rebase --onto test dev fix
First, rewinding head to replay your work on top of it...
Applying: add f
Applying: change f

D:\tmp\gittest\master>git ll fix
974accb (HEAD -> fix) change f
51f8e08 add f
40dcfec (test) change g
dc0ddd2 add g
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

05

如果沒有分支指向<ref-end>,比如執行git rebase --onto fix master dev~1後,HEAD處於遊離狀態,沒有指向任何分支,還需要以下操作:

D:\tmp\gittest\master>git ll dev
87c6bae (dev) change e
8d7cfc9 add e
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

D:\tmp\gittest\master>git rebase --onto fix master dev~1
First, rewinding head to replay your work on top of it...
Applying: add e

D:\tmp\gittest\master>git st
HEAD detached from a355be9
nothing to commit, working tree clean

D:\tmp\gittest\master>git br
* (HEAD detached from a355be9)
  dev
  fix
  master
  test

D:\tmp\gittest\master>git co dev
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  13ea505 add e

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 13ea505

Switched to branch 'dev'

D:\tmp\gittest\master>git reset --hard 13ea505
HEAD is now at 13ea505 add e

D:\tmp\gittest\master>git br
* dev
  fix
  master
  test
D:\tmp\gittest\master>git ll dev
13ea505 (HEAD -> dev) add e
974accb (fix) change f
51f8e08 add f
40dcfec (test) change g
dc0ddd2 add g
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

06

在這裡插入圖片描述

可能的使用場景:

  • 將某一臨時分支的一段提交變基到目標分支
  • 提交較少情況下,可使用git cherry-pick代替
  • 可以移除部分不需要的commit: 執行git rebase --onto dev~3 dev~1 dev,將移除dev~2,dev~1

用法四

變基時保留合併提交:git rebase -i -p --onto <newBase> <upstream> [<branch>]

  • 如果要保留原merge commit,即保留原結構,如圖示Q,需加選項--preserve-merges,簡寫-p

07

08
09

D:\tmp\gittest\master>git ll
f254843 (HEAD -> dev) Merge branch 'fix' into dev
0f22361 (fix) add k
3fb9d97 add i
1e45291 change h
f847d82 add h
13ea505 add e
974accb change f
51f8e08 add f
40dcfec change g
dc0ddd2 add g
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

D:\tmp\gittest\master>git lh
*   f254843 2019-07-23 Merge branch 'fix' into dev [userName]  (HEAD -> dev)
|\
| * 0f22361 2019-07-23 add k [userName]  (fix)
* | 3fb9d97 2019-07-23 add i [userName]
|/
* 1e45291 2019-07-23 change h [userName]
* f847d82 2019-07-23 add h [userName]
* 13ea505 2019-07-23 add e [userName]
* 974accb 2019-07-23 change f [userName]
* 51f8e08 2019-07-23 add f [userName]
* 40dcfec 2019-07-23 change g [userName]
* dc0ddd2 2019-07-23 add g [userName]
* fb38e3f 2019-07-22 add and change d [userName]  (master)
* 30cec3f 2019-07-17 add b [userName]
* 1a17ba1 2019-07-22 add c [userName]
* 4428c06 2019-07-17 add a [userName]

D:\tmp\gittest\master>git rebase -p --onto test 13ea505
Successfully rebased and updated refs/heads/dev.

D:\tmp\gittest\master>git lh
*   f6c89b0 2019-07-23 Merge branch 'fix' into dev [userName]  (HEAD -> dev)
|\
| * 68ad94d 2019-07-23 add k [userName]
* | 3c5453a 2019-07-23 add i [userName]
|/
* f2db9e5 2019-07-23 change h [userName]
* d468e47 2019-07-23 add h [userName]
* bb3eca7 2019-07-23 add j [userName]  (test)
* 13ea505 2019-07-23 add e [userName]
* 974accb 2019-07-23 change f [userName]
* 51f8e08 2019-07-23 add f [userName]
* 40dcfec 2019-07-23 change g [userName]
* dc0ddd2 2019-07-23 add g [userName]
* fb38e3f 2019-07-22 add and change d [userName]  (master)
* 30cec3f 2019-07-17 add b [userName]
* 1a17ba1 2019-07-22 add c [userName]
* 4428c06 2019-07-17 add a [userName]

D:\tmp\gittest\master>git co fix
Switched to branch 'fix'

D:\tmp\gittest\master>git reset --hard 68ad94d
HEAD is now at 68ad94d add k

D:\tmp\gittest\master>git lh dev
*   f6c89b0 2019-07-23 Merge branch 'fix' into dev [userName]  (dev)
|\
| * 68ad94d 2019-07-23 add k [userName]  (HEAD -> fix)
* | 3c5453a 2019-07-23 add i [userName]
|/
* f2db9e5 2019-07-23 change h [userName]
* d468e47 2019-07-23 add h [userName]
* bb3eca7 2019-07-23 add j [userName]  (test)
* 13ea505 2019-07-23 add e [userName]
* 974accb 2019-07-23 change f [userName]
* 51f8e08 2019-07-23 add f [userName]
* 40dcfec 2019-07-23 change g [userName]
* dc0ddd2 2019-07-23 add g [userName]
* fb38e3f 2019-07-22 add and change d [userName]  (master)
* 30cec3f 2019-07-17 add b [userName]
* 1a17ba1 2019-07-22 add c [userName]
* 4428c06 2019-07-17 add a [userName]

如果不加-p選項:

10
11

D:\tmp\gittest\master>git co dev
Switched to branch 'dev'

D:\tmp\gittest\master>git rebase --onto test 13ea505
First, rewinding head to replay your work on top of it...
Applying: add h
Applying: change h
Applying: add i
Applying: add k

D:\tmp\gittest\master>git ll
396ffbd (HEAD -> dev) add k
26f83cf add i
303cc06 change h
9e3c0be add h
bb3eca7 (test) add j
13ea505 add e
974accb change f
51f8e08 add f
40dcfec change g
dc0ddd2 add g
fb38e3f (master) add and change d
30cec3f add b
1a17ba1 add c
4428c06 add a

D:\tmp\gittest\master>git lh
* 396ffbd 2019-07-23 add k [userName]  (HEAD -> dev)
* 26f83cf 2019-07-23 add i [userName]
* 303cc06 2019-07-23 change h [userName]
* 9e3c0be 2019-07-23 add h [userName]
* bb3eca7 2019-07-23 add j [userName]  (test)
* 13ea505 2019-07-23 add e [userName]
* 974accb 2019-07-23 change f [userName]
* 51f8e08 2019-07-23 add f [userName]
* 40dcfec 2019-07-23 change g [userName]
* dc0ddd2 2019-07-23 add g [userName]
* fb38e3f 2019-07-22 add and change d [userName]  (master)
* 30cec3f 2019-07-17 add b [userName]
* 1a17ba1 2019-07-22 add c [userName]
* 4428c06 2019-07-17 add a [userName]

衝突

  • 當遇到衝突時,git rebase會停在第一個有問題的提交,並標記出衝突位置
  • 你可以使用git diff檢視衝突,當解決每個檔案的衝突後,需要執行git add <filename>標記衝突已解決
  • 解決所有衝突後執行git rebase --continue,或者撤銷該命令:git rebase --abort

相關文章