掌握強大的 Git 變基命令
當我與別人談到 Git 時,幾乎每個人都對 ??git rebase 命令?? 有強烈的印象,這個命令讓許多人遇到了問題,而不得不更改目錄、刪除倉庫、然后再重新克隆一個倉庫。我認為這是因為他們誤解了分支是如何工作,遇到了一個非常糟糕的默認界面,還有一些合并沖突把事情搞得一團糟。
怎么找不到 git squash 命令?
如果你曾在本地的倉庫提交過很多次,并希望能把這些提交都合并為一個提交,接下來,我們就來介紹能用什么 Git 命令達到這個目的。Git 稱這個概念為 “壓扁提交squash commits”。我在編寫文檔時發現了這個概念:我花了十幾個提交才修改好我的 Markdown 文檔,但是倉庫的維護者不想看到我的所有嘗試,以免擾亂了該項目的歷史,所以我被告知“需要壓扁你的提交”。
壓扁提交聽起來是一個很有用的方法。但是只有一個問題:我不知道該怎么做。作為 Git 的新手,我做了任何人會做的事情:我去查閱 ??git-squash?
? 的手冊,但我立即遇到了阻礙:
我發現沒有一個名為 ??squash?
? 的 Git 命令,而是被要求 ??運行一個完全獨立的命令:git rebase 命令??,該命令能將我的所有提交最終合并為一個提交。
我知道我碰到一個常見的情形:已經使用工具一段時間的人使用了行話或引用了一個概念,這個概念對他們來說是非常清楚的,但對新手來說就不能明白了。從概念上講,這個情況看起來是這樣的:
of 6 bowls of different Image colored spices, and an arrow pointing to the second image of all the spices blended into one bowl.
我這樣說是為了鼓勵你,你絕對不是第一個或最后一個 被 Git 或談論 Git 的人 弄糊涂的人。你可以要求對方說明白他的意見,并幫助你應該使用的正確命令。倉庫的維護者實際上的意思是,“使用 ??git rebase?
? 命令**,將很多提交壓扁成一個提交”。
現在就來學習 git rebase 命令吧
??git rebase?
? 命令會將一個提交鏈從其第一個父級中刪除,并將其放置在另一個提交鏈的末尾,將兩個提交鏈組合成一個長鏈,而不是兩個并行鏈。我意識到這是一個很復雜的定義。
回想一下 Git 的提交是如何鏈接在一起的,你可以看到,除了初始的 ??main?
?(或 ??master?
?)分支外,任何分支都有一個 父提交parent commit 作為該鏈的 “基礎base”。“變基rebase” 能使另一個鏈中的最后一個提交成為指定分支的新 “基礎提交base commit”。
在 Git 中整合來自不同分支的修改主要有兩種方法:合并merge 以及 變基rebase,你可能更熟悉 ??git merge?
? 命令。接下來,就來看看 [??git-scm.com??] 是如何解釋 ??git merge?
? 和 ??git rebase?
? 的差異:
Image of Git merge versus git rebase shown as numbered bubbles.
在合并示例中,它會把兩個分支的最新快照(??C3?
? 和 ??C4?
?)以及二者最近的共同祖先(??C2?
?)進行三方合并,合并的結果是生成一個新的快照(??C5?
?)。??experiment?
? 的分支指針仍然存在,仍然指向 ??C4?
?。
在變基示例中,它提取在 ??C4?
? 中引入的補丁和修改,然后在 ??C3?
? 的基礎上應用一次,使 ??C3?
? 成為 ??C4?
? 的新父級,并產生了一個名為 ??C4'?
? 的新提交。
(LCTT 譯注:具體的命令如下:
它的原理是首先找到這兩個分支 —— 即當前分支 ??experiment?
?、變基操作的目標基底分支 ??main?
? —— 的最近共同祖先 ??C2?
?,然后對比當前分支相對于該祖先的歷次提交,提取相應的修改并存為臨時文件,然后將當前分支指向目標基底 ??C3?
?,最后以此將之前另存為臨時文件的修改依序應用。)
值得注意的是,分支指針 ??main?
? 沒有移動。要讓 Git 將指針移動到鏈的末尾(由??experiment?
? 指向),你還需要執行合并。
(LCTT 譯注:具體的命令如下:
master 分支的快進合并
此時,??C4'?
? 指向的快照就和上面使用 ??merge?
? 命令的例子中 ??C5?
? 指向的快照一模一樣了。)
??git rebase?
? 并不能替代 ??git merge?
?。??git rebase?
? 是一種用于制作更清晰的歷史記錄,以與 ??git merge?
? 結合使用的工具。
(LCTT 譯注:使用 ??git rebase?
? 命令將提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一樣。)
交互式變基能給你一個更友好的界面!
從命令行執行 ??git rebase?
? 命令,最可怕的地方在于它糟糕的默認界面。運行命令 ??git rebase <target-refr>?
? 要么有效,要么會變得一團糟,因為它沒有太多的反饋或方法來確保它做你想做的事情。幸運的是,??git rebase?
? 命令和許多其他 Git 命令一樣,具有 交互模式interactive mode,你可以使用參數 ??-i?
? 或者 ??-interactive?
? 來使用交互模式。
Image of the Git lens interactive Rebase tool in VS Code.
在使用交互式模式時,??git rebase?
? 會從一個糟糕的黑框界面轉換為一個選項菜單,允許你選擇對正在變基的提交鏈所做的事。對于每個提交,你可以選擇:
- 選用pick:按原樣包含
- 重寫reword:重寫提交消息
- 編寫edit:在變基完成之前對提交中的文件進行進一步更改
- 壓扁squash:將多個提交壓縮成一個提交,保留所有提交消息
- 修理fixup:將多個提交壓縮成一個提交,但只保留最后一個提交消息
- 丟棄drop:丟棄此提交
就我個人而言,我更喜歡 ??VS Code 的開源 GitLens 擴展?? 使用下拉選擇列表布局選項的方式,但 Git 允許你使用任何編輯器選擇這些選項。對于 Emacs 或 Vim 等純文本工具,你需要鍵入選擇,而不是從菜單中選擇,但最終結果仍然是相同的。
何時做變基
知道 何時 做變基與知道 如何 做變基同樣重要。事實上,如果你不在乎你的倉庫歷史提交消息有點混亂的話,那么你可以永遠都不使用 ??git rebase?
? 命令。但是,如果你想要更干凈的歷史提交消息,并且想要更少擾亂你的圖形視圖的提交,那么當你使用 ??git rebase?
? 命令時,有一個重要的經驗法則需要時刻記住:
“不要變基你存儲庫以外的的提交,那些提交可能是別人工作的基礎。”
如果你遵循該準則,不會發生什么大問題的。
簡而言之,如果你讓一個本地分支來完成你的工作,變基是沒有問題的。但一旦該分支被 推送push
希望你會認為上述內容有助于你理解 ??git rebase?
? 命令的工作原理,并能讓你更有信心地使用它。與任何 Git 命令一樣,練習是學習和理解怎么做的唯一方法。我鼓勵你勇敢地嘗試 交互式變基interactive rebase ??git rebase -i <branch name>?
?!
接下來學習 Git cherry-pick 命令吧
大多數開發人員將修改提交到某一分支上,但是之后發現他們一直提交到了錯誤的分支上。理想情況下,他們可以拿走那個提交,然后把它移到正確的分支,這正是 ??git cherry-pick?
? 命令的作用。
??git cherry-pick?
? 命令利用了變基單個提交的方法。這一用法非常常見,以至于有了它自己的命令。
Image of a woman picking a cherry from one tree and putting on another tree.
要使用 ??git cherry-pick?
?,你只需告訴 Git 你要移動到“那個分支”的提交 ID(由 ??HEAD?
? 指向):
如果出現問題,你可以根據 Git 提供的錯誤消息,來進行恢復:
讓 Git 更強大
??git rebase?
? 命令是 Git 實用程序強大的地方之一。你最好在測試倉庫中先練習一下怎么使用,一旦你熟悉了它的概念和工作流程,你就可以給倉庫一個清晰歷史消息記錄了。