[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
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校驗值不同,這無疑會帶來麻煩。
因此,如果把變基當成一種在推送之前清理提交歷史的手段,而且僅僅變基那些尚未公開的提交對象,就沒問題。如果變基那些已經公開的提交對象,并且已經有人基于這些提交對象開展了后續開發工作的話,就會出現叫人沮喪的麻煩。
總結
以上是生活随笔為你收集整理的[Git] Git整理(四) git rebase 的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go语言google pay支付验证订单
- 下一篇: 僵尸基金寒冬大逃杀