讓大家覺得你一次就能寫出完美的程式碼,並讓你的修補程式更容易稽核和合併。
軟體開發是混亂的。有很多錯誤的轉折、有需要修復的錯別字、有需要修正的錯誤、有需要稍後糾正的臨時和粗陋的程式碼,還有在以後的開發過程中發現一次又一次的問題。有了版本控制,在建立“完美”的最終產品(即準備提交給上游的修補程式)的過程中,你會有一個記錄著每一個錯誤轉折和修正的原始記錄。就像電影中的花絮一樣,它們會讓人有點尷尬,有時也會讓人覺得好笑。
如果你使用版本控制來定期儲存你的工作線索,然後當你準備提交稽核的東西時,又可以隱藏所有這些私人草稿工作,並只提交一份單一的、完美的修補程式,那不是很好嗎?git rebase -i
,是重寫歷史記錄的完美方法,可以讓大家覺得你一次就寫出了完美的程式碼!
如果你不熟悉 Git 的複雜性,這裡簡單介紹一下。在幕後,Git 將專案的不同版本與唯一識別符號關聯起來,這個識別符號由父節點的唯一識別符號的雜湊以及新版本與其父節點的差異組成。這樣就形成了一棵修訂樹,每個簽出專案的人都會得到自己的副本。不同的人可以把專案往不同的方向發展,每個方向都可能從不同的分支點開始。
左邊是 origin 版本庫中的主分支,右邊是你個人副本中的私有分支。
有兩種方法可以將你的工作與原始版本庫中的主分支整合起來:一種是使用合併:git merge
,另一種是使用變基:git rebase
。它們的工作方式非常不同。
當你使用 git merge
時,會在主分支(master
)上建立一個新的提交,其中包括所有來自原始位置(origin
)的修改和所有原生的修改。如果有任何衝突(例如,如果別人修改了你也在修改的檔案),則將這些衝突標記出來,並且你有機會在將這個“合併提交”提交到本地版本庫之前解決這些衝突。當你將更改推播回父版本庫時,所有的本地工作都會以分支的形式出現在 Git 版本庫的其他使用者面前。
但是 git rebase
的工作方式不同。它會回滾你的提交,並從主分支(master
)的頂端再次重放這些提交。這導致了兩個主要的變化。首先,由於你的提交現在從一個不同的父節點分支出來,它們的雜湊值會被重新計算,並且任何克隆了你的版本庫的人都可能得到該版本庫的一個殘破副本。第二,你沒有“合併提交”,所以在將更改重放到主分支上時會識別出任何合併衝突,因此,你需要在進行變基之前先修復它們。現在,當你現在推播你的修改時,你的工作不會出現在分支上,並且看起來像是你是在主分支的最新的提交上寫入了所有的修改。
合併提交(左)保留了歷史,而變基(右)重寫歷史。
然而,這兩種方式都有一個缺點:在你準備好分享程式碼之前,每個人都可以看到你在本地處理問題時的所有塗鴉和編輯。這就是 git rebase
的 --interactive
(或簡寫 -i
)標誌發揮作用的地方。
git rebase
的最大優點是它可以重寫歷史。但是,為什麼僅止於假裝你從後面的點分支出來呢?有一種更進一步方法可以重寫你是如何準備就緒這些程式碼的:git rebase -i
,即互動式的 git rebase
。
這個功能就是 Git 中的 “魔術時光機” 功能。這個標誌允許你在做變基時對修訂歷史記錄進行複雜的修改。你可以隱藏你的錯誤! 將許多小的修改合併到一個嶄新的功能修補程式中! 重新排列修改歷史記錄中的顯示順序!
當你執行 git rebase -i
時,你會進入一個編輯器對談,其中列出了所有正在被變基的提交,以及可以對其執行的操作的多個選項。預設的選擇是選擇(Pick
)。
Pick
:會在你的歷史記錄中保留該提交。Reword
:允許你修改提交資訊,可能是修復一個錯別字或新增其它注釋。Edit
:允許你在重放分支的過程中對提交進行修改。Squash
:可以將多個提交合併為一個。當你完成後,只需儲存最終結果,變基操作就會執行。在你選擇修改提交的每個階段(無論是用 reword
、edit
、squash
還是發生衝突時),變基都會停止,並允許你在繼續提交之前進行適當的修改。
上面這個例子的結果是 “One-liner bug fix” 和 “Integate new header everywhere” 被合併到一個提交中,而 “New header for docs website” 和 “D'oh - typo. Fixed” 合併到另一個提交中。就像變魔術一樣,其他提交的工作還在你的分支中,但相關的提交已經從你的歷史記錄中消失了!
這使得使用 git send-email
或者用你新整理好的修補程式集在父版本庫中建立一個拉取請求,然後來提交一個乾淨的修補程式給上游專案變得很容易。這有很多好處,包括讓你的程式碼更容易稽核,更容易接受,也更容易合併。