Git Rebase操作
概括
rebase翻譯過來為“變基”,可以理解為改變基礎,它可以用於分支合併和修改提交記錄。
合併分支的區別
我們知道merge操作也可以用於分支合併,但是其和rebase操作有著明顯的不同。假定有一個分支foo在B提交處檢出了分支bar,接著兩個分支各自前進出現了分叉,現在要將bar分支合併回foo分支。
A--B--C----E foo
\
----D bar
首先使用merge操作,在foo分支下執行git merge bar
命令,git就會在當前分支(即foo分支)下生成一個新的commit節點,從而實現分支的合併。
A--B--C----E---F foo \ / ----D---- bar
而reabse操作合併分支的過程與之明顯不同,我們在foo分支下執行git rebase bar
命令。
現在來看看rebase操作的合併過程,它先是會暫存當前分支上從分叉開始之後commit節點,然後回退到分叉開始的節點。
stage: C, E
A--B foo
\
----D bar
接著bar分支上的所有commit被移動到foo分支上,最後再合併之前暫存的節點。
A--B--D--C'--E' foo
\
----D bar
可以看到rebase操作合併之後的提交記錄是一條線,而不像merge操作一樣變成稜形,因此rebase操作合併分支會讓提交記錄看起來更加簡潔。
工作流程
接下來基於rebase操作設計一個簡單的分支協同開發流程,流程分為檢出分支前和分支上提交後兩個部分。
檢出之前先在dev分支上pull以獲取分支上最新提交,這樣可以減少後續提交時的衝突,當然該步驟不是必須的。
接著便可以檢出分支來進行開發,下面是該流程的命令示例。
[origin/dev => dev => ${new_branch}]
(dev)$ git pull
(dev)$ git checkout -b ${new_branch}
開發完成並在本地分支上提交後,就可以進行分支的合併了,首先還是先pull拉取dev分支上的最新提交。
接著分為兩種情況,一種是沒有其他人提交顯示“Already up to date.”,這時候就直接切換到dev分支,然後執行rebase操作合併分支,最後提交到遠端。
還有一種當然是有人提交過了,這時候就要先執行rebase操作將dev分支合併到當前開發的分支,接著可能就會出現衝突,需要手動修改衝突後執行git add ${conflict_file}
及git rebase --continue
完成衝突修改。
衝突修改之後的操作和前面是一樣的這裡不再敘述,詳細請參考下面的命令示例。
[${new_branch} => dev => origin/dev]
(${new_branch})$ git pull origin dev:dev
=========IF NOT UP TO DATE=========
(${new_branch})$ git rebase dev
(${new_branch})$ <fix the conflict>
===================================
($(new_branch))$ git checkout dev
(dev)$ git rebase ${new_branch}
(dev)$ git push
修改提交記錄
rebase操作除了合併分支,還可以用於提交記錄的修改。
當然一般不推薦在多人協同開發的分支上修改提交記錄,一來是修改記錄時的誤操作可能會導致提交資料丟失。
二來是使用rebase修改提交記錄後,需要使用--force
選項強制推送到遠端,假設剛好有其他人提交了程式碼而你沒有及時更新,就會導致其他人的程式碼被你覆蓋掉。
rebase操作修改提交記錄其實也很簡單,先是執行命令git rebase -i <START> [END]
,選項-i
指的是使用互動式操作方式,後面兩個引數是操作的範圍。
這裡<START>
指的是開始的提交記錄,而[END]
則是結束的提交記錄,如果結束的提交記錄省略則預設為HEAD。注意這裡範圍是前面為開區間後面為閉區間,用區間表示為(START, END]
。提交記錄既可以用雜湊值表示,也可以用頭指標的偏移值表示,例如HEAD~1
表示頭指標指向的上一個提交。
上面命令執行完之後,就會出現如下格式的文字,同commit一樣都是使用編輯器開啟,修改完成後儲存即可。
pick acaa54 Add `demo.md`
pick baed03 Add one line
每一行代表一個提交記錄,第一個欄位是對提交記錄的操作命令,第二個欄位是提交記錄的雜湊值,最後則是提交記錄的註釋。
下面是完整的操作命令和其作用,預設為pick命令即表示使用該提交記錄且不做更改。
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
總結
雖然rebase操作功能強大,但掌握其正確使用才是重中之重。還有使用rebase還是merge合併分支仍有爭議,兩種方式各有利弊,這需要團隊內部進行權衡選擇。