使用 git rebase -i 來修改你的 Git 提交歷史
讓大家覺得你一次就能寫出完美的代碼,并讓你的補丁更容易審核和合并。
軟件開發(fā)是混亂的。有很多錯誤的轉(zhuǎn)折、有需要修復的錯別字、有需要修正的錯誤、有需要稍后糾正的臨時和粗陋的代碼,還有在以后的開發(fā)過程中發(fā)現(xiàn)一次又一次的問題。有了版本控制,在創(chuàng)建“完美”的最終產(chǎn)品(即準備提交給上游的補丁)的過程中,你會有一個記錄著每一個錯誤轉(zhuǎn)折和修正的原始記錄。就像電影中的花絮一樣,它們會讓人有點尷尬,有時也會讓人覺得好笑。
如果你使用版本控制來定期保存你的工作線索,然后當你準備提交審核的東西時,又可以隱藏所有這些私人草稿工作,并只提交一份單一的、完美的補丁,那不是很好嗎?git rebase -i
,是重寫歷史記錄的完美方法,可以讓大家覺得你一次就寫出了完美的代碼!
git rebase 的作用是什么?
如果你不熟悉 Git 的復雜性,這里簡單介紹一下。在幕后,Git 將項目的不同版本與唯一標識符關(guān)聯(lián)起來,這個標識符由父節(jié)點的唯一標識符的哈希以及新版本與其父節(jié)點的差異組成。這樣就形成了一棵修訂樹,每個簽出項目的人都會得到自己的副本。不同的人可以把項目往不同的方向發(fā)展,每個方向都可能從不同的分支點開始。
左邊是 origin 版本庫中的主分支,右邊是你個人副本中的私有分支。
有兩種方法可以將你的工作與原始版本庫中的主分支整合起來:一種是使用合并:git merge
,另一種是使用變基:git rebase
。它們的工作方式非常不同。
當你使用 git merge
時,會在主分支(master
)上創(chuàng)建一個新的提交,其中包括所有來自原始位置(origin
)的修改和所有本地的修改。如果有任何沖突(例如,如果別人修改了你也在修改的文件),則將這些沖突標記出來,并且你有機會在將這個“合并提交”提交到本地版本庫之前解決這些沖突。當你將更改推送回父版本庫時,所有的本地工作都會以分支的形式出現(xiàn)在 Git 版本庫的其他用戶面前。
但是 git rebase
的工作方式不同。它會回滾你的提交,并從主分支(master
)的頂端再次重放這些提交。這導致了兩個主要的變化。首先,由于你的提交現(xiàn)在從一個不同的父節(jié)點分支出來,它們的哈希值會被重新計算,并且任何克隆了你的版本庫的人都可能得到該版本庫的一個殘破副本。第二,你沒有“合并提交”,所以在將更改重放到主分支上時會識別出任何合并沖突,因此,你需要在進行變基之前先修復它們?,F(xiàn)在,當你現(xiàn)在推送你的修改時,你的工作不會出現(xiàn)在分支上,并且看起來像是你是在主分支的最新的提交上寫入了所有的修改。
合并提交(左)保留了歷史,而變基(右)重寫歷史。
然而,這兩種方式都有一個缺點:在你準備好分享代碼之前,每個人都可以看到你在本地處理問題時的所有涂鴉和編輯。這就是 git rebase
的 --interactive
(或簡寫 -i
)標志發(fā)揮作用的地方。
git rebase -i 登場
git rebase
的最大優(yōu)點是它可以重寫歷史。但是,為什么僅止于假裝你從后面的點分支出來呢?有一種更進一步方法可以重寫你是如何準備就緒這些代碼的:git rebase -i
,即交互式的 git rebase
。
這個功能就是 Git 中的 “魔術(shù)時光機” 功能。這個標志允許你在做變基時對修訂歷史記錄進行復雜的修改。你可以隱藏你的錯誤! 將許多小的修改合并到一個嶄新的功能補丁中! 重新排列修改歷史記錄中的顯示順序!
output of git rebase -i
當你運行 git rebase -i
時,你會進入一個編輯器會話,其中列出了所有正在被變基的提交,以及可以對其執(zhí)行的操作的多個選項。默認的選擇是選擇(Pick
)。
Pick
:會在你的歷史記錄中保留該提交。Reword
:允許你修改提交信息,可能是修復一個錯別字或添加其它注釋。Edit
:允許你在重放分支的過程中對提交進行修改。Squash
:可以將多個提交合并為一個。- 你可以通過在文件中移動來重新排序提交。
當你完成后,只需保存最終結(jié)果,變基操作就會執(zhí)行。在你選擇修改提交的每個階段(無論是用 reword
、edit
、squash
還是發(fā)生沖突時),變基都會停止,并允許你在繼續(xù)提交之前進行適當?shù)男薷摹?/p>
上面這個例子的結(jié)果是 “One-liner bug fix” 和 “Integate new header everywhere” 被合并到一個提交中,而 “New header for docs website” 和 “D'oh - typo. Fixed” 合并到另一個提交中。就像變魔術(shù)一樣,其他提交的工作還在你的分支中,但相關(guān)的提交已經(jīng)從你的歷史記錄中消失了!
這使得使用 git send-email
或者用你新整理好的補丁集在父版本庫中創(chuàng)建一個拉取請求,然后來提交一個干凈的補丁給上游項目變得很容易。這有很多好處,包括讓你的代碼更容易審核,更容易接受,也更容易合并。