[Git] Git整理(四) git rebase 的使用
概述
在之前總結分支相關內容時說道,合併兩個分支的提交可以使用git merge
,然而除了這種方式之外,還有一種方式就是使用git rebase
,這兩種方式的最終結果都相同,但是合併歷史卻不同;git merge
是將兩個分支做一個三方合併(如果不是直接上游分支),這樣一來,檢視提交歷史記錄,可能會顯得非常凌亂。git rebase
則會將當前分支相對於基低分支的所有提交生成一系列補丁,然後放到基底分支的頂端,從而使得提交記錄變稱一條直線,非常整潔。
git merge 和 git rebase 區別
假設現在本地倉庫中有兩個分支:master
分支和branch1
分支,提交歷史用圖來表示如下:
現在要合併branch1到master分支,如果使用git merge
則執行如下命令:
$ git checkout master
$ git merge branch1
合併後檢視提交歷史如下:
$ 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
使用圖來表示,本地倉庫提交歷史如下:
現在我們使用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
合併後檢視提交歷史如下:
$ 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
。
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
上述命令的意思是:取出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
或者可以選擇終止變基:
$ git rebase --abort
或者跳過該patch:
$ git rebase --skip
git rebase -i
該命令相比其他命令,使用頻率要高得多。git rebase -i <commitid>
可以進行互動式變基,相比於git rebase <branch>
用來變基,它經常用來操作當前分支的提交,git會將<commitid>-HEAD
之間的提交列在一個變基指令碼中,每一筆提交根據使用者設定的命令,會進行不同的操作,如修改提交資訊、移除指定提交、合併兩個提交為一個(壓縮提交)、拆分提交等。
如,要對最近4次提交進行重新操作,則:
$ git rebase -i HEAD~4
此時將會彈出如下形式的變基指令碼:
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校驗值不同,這無疑會帶來麻煩。
因此,如果把變基當成一種在推送之前清理提交歷史的手段,而且僅僅變基那些尚未公開的提交物件,就沒問題。如果變基那些已經公開的提交物件,並且已經有人基於這些提交物件開展了後續開發工作的話,就會出現叫人沮喪的麻煩。
參考:
Git 官網
相關文章
- Git rebase 與 Git merge 的使用Git
- git rebase的使用Git
- git rebase 和 git mergeGit
- [Git]rebaseGit
- git rebaseGit
- Git -- RebaseGit
- git學習之git rebaseGit
- Git——rebase命令Git
- git rebase -iGit
- git rebase 流程Git
- git rebase masterGitAST
- git在工作中正確的使用方式----git rebase篇Git
- 學會使用 git-rebaseGit
- git-rebase進階使用Git
- Git基礎命令之git rebase命令Git
- 如何理解git rebase?Git
- git:rebase(變基)的使用和理解Git
- git rebase(變基)—— Git 學習筆記 19Git筆記
- 詳解git rebase,讓你走上git大神之路Git
- 你在開發過程中使用Git Rebase還是Git Merge?Git
- Git應用詳解第九講:Git cherry-pick與Git rebaseGit
- git rebase --onto 的奇妙用法Git
- git merge和rebase的區別Git
- GIT使用rebase和merge的正確姿勢Git
- 撤銷rebase與git原理Git
- 徹底搞懂 Git-RebaseGit
- 面試官:主說 Git Rebase 和 Git Merge 差異面試Git
- 你根本不懂rebase-使用rebase打造可讀的git graphGit
- 對git的rebase(變基)的理解Git
- Git中merge和rebase的區別Git
- git命令整理Git
- GIt操作整理Git
- 使用 git add -p 整理 patchGit
- (轉載)git pull origin master與git pull --rebase origin master的區別GitAST
- Git教程十九:分支管理之RebaseGit
- GIT使用過程出現(master|REBASE 1/10)GitAST
- Git基礎整理Git
- Git學習整理Git