[Git] Git整理(四) git rebase 的使用

概述
在以前總結分支相關內容時說道,合併兩個分支的提交可使用git merge,然而除了這種方式以外,還有一種方式就是使用git rebase,這兩種方式的最終結果都相同,可是合併歷史卻不一樣;git merge是將兩個分支作一個三方合併(若是不是直接上游分支),這樣一來,查看提交歷史記錄,可能會顯得很是凌亂。git rebase則會將當前分支相對於基低分支的全部提交生成一系列補丁,而後放到基底分支的頂端,從而使得提交記錄變稱一條直線,很是整潔。git

git merge 和 git rebase 區別
假設如今本地倉庫中有兩個分支:master分支和branch1分支,提交歷史用圖來表示以下:shell

如今要合併branch1到master分支,若是使用git merge則執行以下命令:服務器

$ git checkout master
$ git merge branch1

合併後查看提交歷史以下:app

$ git log --graph --pretty="oneline"
*   fe8799e0aec30e388306883960b4cf438d3f1ec4 Merge branch 'branch1'
|\  
| * cf31255da6e84acc6f6840e3ceb0fd3129e2d73e UserA commit 3--branch1
| * 5c2d1c938f8e5f98dccaa0a5ab6222bd6b1cd75d UserA commit 2--branch1
* | 284aa3eb6c405411584d682a1387118fe92e4821 Usera commit master
* | 967fca58deb914ad1cda9ff84291fd946045207d Usera commit master
|/  
* d989fc50530918b3b7b0ed68b31d6751c2302875 UserA commit 1

使用圖來表示,本地倉庫提交歷史以下:this


如今咱們使用git rebase合併原來的master分支和branch1分支,假設當前分支爲branch1,基地分支爲master:翻譯

$ git checkout branch1
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: UserA commit 2--branch1
Applying: UserA commit 3--branch1
Applying: Usera commit master
Applying: Usera commit master

合併後查看提交歷史以下:3d

$ git log --graph --pretty="oneline"
* 6cf95c391ba7d43d0f5d95300130a43816af82c8 Usera commit master
* 63def8a8740b9b3c9f6c09ae49ba72faa9446cf6 Usera commit master
* 33049864f83a686bff9b2a2d8626427653a16f22 UserA commit 3--branch1
* 14ac1cac7357ccf35581c89e099793260264d3ea UserA commit 2--branch1
* d989fc50530918b3b7b0ed68b31d6751c2302875 UserA commit 1

使用圖來表示,本地倉庫提交歷史以下:指針

能夠看到,如今branch1分支上相對於master分支的提交,提交到了master分支的頂端,如此一來整個提交記錄保持在一條直線上。這就是git rebase。rest

rebase原理
git rebase <branch>的原理是:找到兩個分支最近的共同祖先,根據當前分支(上例中branch1)的提交歷史生成一系列補丁文件,而後以基地分支最後一個提交爲新的提交起始點,應用以前生成的補丁文件,最後造成一個新的合併提交。從而使得變基分支成爲基地分支的直接下游。rebase通常被翻譯爲變基。對象

當branch1分支完成變基後,直接變成了master分支的下游了,這時切換到master分支,直接經過git merge便可將branch1分支的合併到master分支上:

$ git merge branch1 
Updating ff7658d..d6168dc
Fast-forward
 test.txt | 1 +
 1 file changed, 1 insertion(+)

掌握了rebase基本原理後,接下來對該命令一些參數進行下總結。

git rebase branchA branchB
首先會取出branchB,將branchB中的提交放在branchA的頂端,通常branchB爲當前分支,能夠不指定。

假設當前本地倉庫提交歷史以下,且處於topic分支:

     A---B---C topic
    /
D---E---F---G master

此時咱們使用git rebase將兩個分支的提交合併到master分支的頂端:

$ git rebase master
# 或者
$ git rebase master topic

此時,提交歷史將變爲:

             A'--B'--C' topic
            /
D---E---F---G master

git rebase --onto
若是要在合併兩分支時須要跳過某一分支的提交,這時就可使用--onto參數了。好比,假設當前本地倉庫提交歷史以下:

A---B---E---F---G  master
    \
     C---D---H---I---J  next
                      \
                       K---L---M  topic

此時topic分支的上游分支是next分支,若是要將topic分支上的提交(K,M,L)跳過next分支,直接放到master分支上,就須要加上--onto參數:

$ git rebase --onto master next topic
1
上述命令的意思是:取出topic分支,找出topic和next分支的共同祖先以後的提交,而後放在master分支上,執行後提交歷史變爲以下:

A---B---E---F---G  master
    \            \
     \            K'---L'---M'  topic 
      \     
       C---D---H---I---J  next
                      
git rebase --continue/abort/skip
這三個命令分別表示:繼續執行變基操做、終止變基、跳過某一文件繼續進行。在rebase的過程當中,有可能會出現文件衝突,好比:

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: [Description]:test1
Using index info to reconstruct a base tree...
M    test.txt
Falling back to patching base and 3-way merge...
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
error: Failed to merge in the changes.
Patch failed at 0001 [Description]:test1
The copy of the patch that failed is found in: .git/rebase-apply/patch

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".


這種狀況下,首先要解決衝突,解決衝突後能夠選擇繼續執行rebase或者結束rebase,通常的作法爲:

$ git add filename
$ git rebase --continue
1
2
或者能夠選擇終止變基:

$ git rebase --abort
1
或者跳過該patch:

$ git rebase --skip
1
git rebase -i
該命令相比其餘命令,使用頻率要高得多。git rebase -i <commitid>能夠進行交互式變基,相比於git rebase <branch>用來變基,它常常用來操做當前分支的提交,git會將<commitid>-HEAD之間的提交列在一個變基腳本中,每一筆提交根據用戶設置的命令,會進行不一樣的操做,如修改提交信息、移除指定提交、合併兩個提交爲一個(壓縮提交)、拆分提交等。

如,要對最近4次提交進行從新操做,則:

$ git rebase -i HEAD~4
1
此時將會彈出以下形式的變基腳本:

  1 pick af98479 [Description]:test4
  2 pick 3cc9d66 test3
  3 pick a7e819e usera commit03 branch2
  4 pick efc5b15 usera commit04 branch2
  5 
  6 # Rebase de7b118..efc5b15 onto de7b118 (4 command(s))
  7 #
  8 # Commands:
  9 # p, pick = use commit
 10 # r, reword = use commit, but edit the commit message
 11 # e, edit = use commit, but stop for amending
 12 # s, squash = use commit, but meld into previous commit
 13 # f, fixup = like "squash", but discard this commit's log message
 14 # x, exec = run command (the rest of the line) using shell
 15 # d, drop = remove commit
 16 #
 17 # These lines can be re-ordered; they are executed from top to bottom.
 18 #
 19 # If you remove a line here THAT COMMIT WILL BE LOST.
 20 #
 21 # However, if you remove everything, the rebase will be aborted.
 22 #
 23 # Note that empty commits are commented out


這裏咱們能夠修改pick爲下面給出的其餘命令,好比若是要修改提交信息,就使用r或reword,各指令的含義以下:

p,pick:直接使用該次提交
r,reword:使用該次提交,但從新編輯提交信息
e,edit:中止到該次提交,經過`git commit --amend`追加提交,完畢以後不要忘記使用`git rebase --continue`完成這此rebase
s,squash,壓縮提交,將和上一次提交合併爲一個提交
x,exec,運行命令
d,drop,移除此次提交

接下來咱們看看他們如何使用。

1.修改多個提交信息
使用git commit --amend在最近一次提交上追加提交,所以可使用該命令來修改最後一次的提交信息,若是要修改作個提交信息,須要git rebase -i <commitid>打開變基腳本後在須要修改信息的提交上執行reword操做,好比如下示例:

查看最近四次提交記錄

$ git log --oneline -4
d0a80c2 02-b2
b6c6595 01-b2
ea2f366 b2
49afab9 commit branch3w

提交信息很是不人性化,所以對以上幾個提交記錄修改提交信息,將默認的pick改成r或者reword:

$ git rebase -i HEAD~4

  1 r 49afab9 commit branch3w
  2 r ea2f366 b2
  3 r b6c6595 01-b2
  4 r d0a80c2 02-b2
  5 # ......

保存退出後,會彈出編譯器輸入提交信息,輸入完畢後:

$ git rebase -i HEAD~4
[detached HEAD afeaed3] commit first
 Date: Wed Jun 27 20:26:33 2018 +0800
 1 file changed, 1 insertion(+), 1 deletion(-)
[detached HEAD 528910c] commit second
 Date: Wed Jun 27 20:29:07 2018 +0800
 1 file changed, 1 deletion(-)
[detached HEAD 0e09a0f] commit 3th
 Date: Wed Jun 27 20:29:25 2018 +0800
 1 file changed, 1 deletion(-)
[detached HEAD eaed13d] commit 4th
 Date: Wed Jun 27 20:29:35 2018 +0800
 1 file changed, 1 deletion(-)
Successfully rebased and updated detached HEAD.


再次查看提交log:

$ git log --oneline -4
eaed13d commit 4th
0e09a0f commit 3th
528910c commit second
afeaed3 commit first

利用git rebase -i <commitid>和reword就能夠完成修改屢次提交信息了。

2.從新排序提交
改變變基腳本中的順序就能夠對以前的提交從新排序,如:
選擇最近4次提交進行處理:

$ git rebase -i HEAD~4
此時會打開變基腳本,在腳本中將second此次提交放在最後一次提交中:

  1 pick ecd66f5 commit first
  2 pick 7dbfe25 commit 3th
  3 pick 82ba6a6 commit 4th
  4 pick a77e06e commit second
  5 # .....

保存退出,查看提交log,發現second變爲最後一次提交:

$ git log --oneline -4
fe15bdb commit second
18fa9a9 commit 4th
d08c408 commit 3th
ecd66f5 commit first

3.壓縮提交
若是要壓縮兩個提交爲一次,須要git rebase -i <commitid>打開變基腳本後在須要壓縮的提交上執行squash操做,當保存退出後,會將該筆提交和上一筆提交壓縮爲一個提交,好比:

先查看當前提交記錄:

$ git log --oneline -4
fe15bdb commit second
18fa9a9 commit 4th
d08c408 commit 3th
ecd66f5 commit first

如今咱們將4th和3th這兩筆提交壓縮爲一筆提交,在執行git rebase -i HEAD~4後在變基腳本中作以下修改:

  1 pick ecd66f5 commit first
  2 pick d08c408 commit 3th
  3 squash 18fa9a9 commit 4th
  4 pick fe15bdb commit second

保存退出後,輸入一下提交信息,成功後再查看log:

$ git log --oneline -4
cf4159b commit second
9d73407 commit----compress 3th and 4th
ecd66f5 commit first

說明合併成功了,若是要對多個提交進行合併壓縮,則能夠按照以下的方式:

  1 pick ecd66f5 commit first
  2 squash d08c408 commit 3th
  3 squash 18fa9a9 commit 4th
  4 pick fe15bdb commit second

這表示會將first、3th、4th進行合併。

4.拆分提交
若是要將一次提交拆分爲屢次提交,則能夠將變基腳本中對應提交的指令修改成edit。拆分一個提交會撤消這個提交,而後屢次地、部分地、暫存與提交直到完成你所需次數的提交。好比下面示例:

首先查看提交記錄:

$ git log --oneline -4
2a5c2aa commit 4th
f2ceb0f commit 3th
20fe2f9 commit second
c51adbe commit first

如今修改3th此次提交,將此次提交拆分爲屢次提交,首先執行git rebase -i HEAD~3,而後在變基腳本中將該次提交指令改成edit:

  1 pick 20fe2f9 commit second
  2 edit f2ceb0f commit 3th
  3 pick 2a5c2aa commit 4th
  # ......

保存退出後,再次查看提交記錄:

$ git log --oneline -3
f2ceb0f commit 3th
20fe2f9 commit second
c51adbe commit first

也就是說,3th此次提交是如今最近的一次提交了,咱們要拆分此次提交,那就就要重置此次提交,讓HEAD指針指向上一次提交:

$ git reset HEAD~
如今進行屢次提交:

$ git add .
$ git commit -m "split commit3th---1"
$ git add .
$ git commit -m "split commit3th---2"
......
$ git add .
$ git commit -m "split commit3th---n"

知足本身拆分後,繼續完成此次rebase:

$ git rebase --continue
最後查看提交記錄,原來的提交被移除,新增了三條:

$ git log --oneline -6
1df4a4d split 3th----3
1c22d70 split 3th----2
dbc7d91 split 3th----1
20fe2f9 commit second
c51adbe commit first
5.移除提交
若是要移除某次提交,能夠在變基腳本中將對應提交指令改成drop,或者直接乾脆刪除,好比要移除上例中新家的三個記錄:

  1 pick c51adbe commit first
  2 pick 20fe2f9 commit second
  3 drop dbc7d91 split 3th----1
  4 drop 1c22d70 split 3th----2
  5 #pick 1df4a4d split 3th----3
查看提交記錄:

$ git log --oneline -6 20fe2f9 commit second c51adbe commit first rebase的風險 一旦分支中的提交對象發佈到公共倉庫,就千萬不要對該分支進行變基操做。 由於不論是git rebase <branch>仍是git rebase <commitid>,都重置了提交的SHA-1校驗和,當你將本地變基後的提交推送到遠端後,別人從服務器同步代碼時,因爲相同的內容卻有不一樣的SHA-1校驗值,所以會再此進行一次合併,因而就會有兩個做者、commit信息徹底相同的提交,可是SHA-1校驗值不一樣,這無疑會帶來麻煩。 所以,若是把變基當成一種在推送以前清理提交歷史的手段,並且僅僅變基那些還沒有公開的提交對象,就沒問題。若是變基那些已經公開的提交對象,而且已經有人基於這些提交對象開展了後續開發工做的話,就會出現叫人沮喪的麻煩。

相關文章
相關標籤/搜索