使用 Git 解決錯誤的指南(第 2 部分)

已發表: 2022-03-10
快速總結↬錯誤。 這些殘忍的惡棍甚至沒有停留在美麗的軟件開發世界。 但是,雖然我們無法避免犯錯,但我們可以學會撤消它們! 本文將展示適合您日常使用 Git 的工具。 您可能還想查看該系列的第一篇文章。

在我們“用 Git 解決錯誤”系列的第二部分中,我們將再次勇敢地直視危險:我準備了四個新的世界末日場景——當然,包括一些拯救我們脖子的聰明方法! 但在我們深入研究之前:查看之前關於 Git 的文章,了解更多幫助您消除 Git 錯誤的自救方法!

我們走吧!

使用 Reflog 恢復已刪除的分支

你有沒有刪除過一個分支,不久之後,你意識到你不應該這樣做? 萬一您不知道這種感覺,我可以告訴您,這不是一種好感覺。 當你想到那個分支提交的所有辛勤工作,你現在丟失的所有有價值的代碼時,悲傷和憤怒的混合物在你身上蔓延。

幸運的是,有一種方法可以使該分支起死回生——借助名為“Reflog”的 Git 工具。 我們在本系列的第一部分中使用了這個工具,但這裡有一點更新:Reflog 就像一個日誌,Git 記錄了本地存儲庫中 HEAD 指針的每一次移動。 換句話說,不那麼討厭的話:任何時候你結帳、提交、合併、變基、挑選等,都會創建一個日記條目。 這使得 Reflog 在出現問題時成為完美的安全網!

我們來看一個具體的例子:

 $ git branch * feature/login master

我們可以看到我們當前已經簽出了我們的分支feature/login 。 假設這是我們要刪除的分支(無意中)。 然而,在我們這樣做之前,我們需要切換到不同的分支,因為我們無法刪除當前的 HEAD 分支!

 $ git checkout master $ git branch -d feature/login

我們有價值的特性分支現在已經消失了——我會給你一點時間來 (a) 了解我們錯誤的嚴重性和 (b) 哀悼一下。 等你擦乾眼淚,我們得想辦法把這根樹枝帶回來! 讓我們打開 Reflog(只需鍵入git reflog )並查看它為我們存儲的內容:

Git 的 Reflog 協議我們本地存儲庫中的所有主要操作
Git 的 Reflog 協議我們本地存儲庫中的所有主要操作。 (大預覽)

這裡有一些註釋可以幫助您理解輸出:

  • 首先,您需要知道 Reflog 按時間順序對其條目進行排序:最新的條目位於列表頂部。
  • 最頂層(因此也是最新的)項目是我們在刪除分支之前執行的git checkout命令。 它被記錄在 Reflog 中,因為它是 Reflog 忠實記錄的這些“HEAD 指針移動”之一。
  • 為了挽回我們的嚴重錯誤,我們可以簡單地回到之前的狀態——這也清楚地記錄在 Reflog 中!

所以讓我們嘗試一下,通過創建一個新分支(名稱為“丟失”分支),該分支從這個“之前”狀態 SHA-1 哈希開始:

 $ git branch feature/login 776f8ca

瞧! 您會很高興看到我們現在已經恢復了我們看似丟失的分支!

如果您使用的是“Tower”之類的 Git 桌面 GUI,您可以使用一個不錯的快捷方式:只需按鍵盤上的CMD + Z即可撤消上一個命令——即使您剛剛暴力刪除了一個分支!

像 Tower 這樣的桌面 GUI 可以使撤消錯誤的過程更容易。
跳躍後更多! 繼續往下看↓

將提交移動到不同的分支

在許多團隊中,有一個協議是不提交長期運行的分支,如maindevelop :像這樣的分支應該只通過集成(例如合併或變基)接收新的提交。 然而,當然,錯誤是不可避免的:儘管如此,我們有時會忘記並提交這些分支! 那麼我們如何才能清理我們製造的爛攤子呢?

將提交移動到其正確的目標分支
我們的提交落在了錯誤的分支上。 我們如何將它移動到正確的目標分支? (大預覽)

幸運的是,這些類型的問題很容易得到糾正。 讓我們捲起袖子開始工作吧。

第一步是切換到正確的目標分支,然後過度使用cherry-pick命令移動提交:

 $ git checkout feature/login $ git cherry-pick 776f8caf

您現在將在所需的分支上進行提交,它應該首先出現在該位置。 驚人的!

但是還有一件事要做:我們需要清理一開始它意外降落的分支! 可以這麼說, cherry-pick命令創建了提交的副本——但原始版本仍然存在於我們長期運行的分支中:

正確分支上的提交副本,但仍顯示原件位於錯誤分支上
我們已經成功地在正確的分支上創建了提交的副本,但原件仍然在這裡——在錯誤的分支上。 (大預覽)

這意味著我們必須切換回我們長期運行的分支並使用git reset來刪除它:

 $ git checkout main $ git reset --hard HEAD~1

如您所見,我們在這裡使用git reset命令擦除錯誤的提交。 HEAD~1參數告訴 Git “返回 HEAD 之後的 1 個修訂版”,有效地從該分支的歷史記錄中刪除最頂層(在我們的例子中:不需要的)提交。

瞧:提交現在應該放在最開始的地方我們長期運行的分支是乾淨的——就好像我們的錯誤從未發生過一樣!

編輯舊提交的消息

將拼寫錯誤偷偷帶入提交消息太容易了——直到很久以後才發現。 在這種情況下, git commit的舊--amend選項不能用來解決這個問題,因為它只適用於最後一次提交。 為了更正任何比這更早的提交,我們必須求助於一個名為“Interactive Rebase”的 Git 工具。

值得更改的提交消息
這是一個值得更改的提交消息。 (大預覽)

首先,我們必須告訴 Interactive Rebase 我們要編輯提交歷史的哪一部分。 這是通過給它提供一個提交哈希來完成的:我們想要操作的那個的提交。

 $ git rebase -i 6bcf266b

然後將打開一個編輯器窗口。 它包含我們在命令中作為交互式 Rebase 基礎提供的提交之後的所有提交的列表:

顯示我們在 Interactive Rebase 會話中選擇進行編輯的提交範圍
我們在 Interactive Rebase 會話中選擇用於編輯的提交範圍。 (大預覽)

在這裡,重要的是不要跟隨你的第一個衝動:在這一步中,我們還沒有編輯提交消息。 相反,我們只告訴 Git 我們想要對哪些提交進行什麼樣的操作。 非常方便的是,在此窗口底部的註釋中有一個動作關鍵字列表。 對於我們的例子,我們用reword標記第 1 行(從而替換標準的pick )。

在此步驟中剩下要做的就是保存並關閉編輯器窗口。 作為回報,將打開一個新的編輯器窗口,其中包含我們標記的提交的當前消息。 現在終於到了進行編輯的時候了!

整個過程一目了然:

使用 Interactive Rebase 從頭到尾編輯舊提交的消息。

糾正一個破碎的提交(以一種非常優雅的方式)

最後,我們將看一下fixup ,這是撤消工具的瑞士軍刀。 簡而言之,它允許您在事後修復損壞/不完整/不正確的提交。 它確實是一個很棒的工具,原因有兩個:

  1. 問題是什麼並不重要。
    您可能忘記了添加文件、應該刪除了某些內容、進行了不正確的更改,或者只是拼寫錯誤。 fixup適用於所有這些情況!
  2. 它非常優雅。
    我們對提交中的錯誤的正常、本能反應是生成一個的提交來解決問題。 這種工作方式,無​​論看起來多麼直觀,都會讓你的提交歷史看起來非常混亂,很快。 你有“原始”提交,然後這些小的“創可貼”提交修復了原始提交中出現的問題。 你的歷史中充斥著小的、毫無意義的創可貼提交,這使得你很難理解你的代碼庫中發生了什麼。
如果你不斷用所謂的創可貼提交來修復小錯誤,你的提交歷史可能很難閱讀
使用“創可貼提交”不斷修復小錯誤會使您的提交歷史非常難以閱讀。 (大預覽)

這就是fixup的用武之地。它允許您仍然進行這種更正的創可貼提交。 但是神奇的地方來了:然後它將它應用於原始的、損壞的提交(以這種方式修復它),然後完全丟棄醜陋的創可貼提交!

Fixup 將您的更正應用於原始提交,然後處理多餘的創可貼提交
Fixup 將您的更正應用於原始提交,然後處理多餘的創可貼提交。 (大預覽)

我們可以一起看一個實際的例子! 假設這裡選擇的提交被破壞了。

以優雅的方式修復選定的錯誤提交
選擇的提交是不正確的——我們將以一種優雅的方式修復它。 (大預覽)

假設我已經在一個名為error.html的文件中準備了可以解決問題的更改。 這是我們需要做的第一步:

 $ git add error.html $ git commit --fixup 2b504bee

我們正在創建一個新的提交,但我們告訴 Git 這是一個特殊的提交:它是對具有指定 SHA-1 哈希(在本例中為2b504bee )的舊提交的修復。

現在,第二步是啟動 Interactive Rebase 會話——因為fixup屬於 Interactive Rebase 的大工具集。

 $ git rebase -i --autosquash 0023cddd

關於這個命令有兩件事值得解釋。 首先,為什麼我在這裡提供0023cddd作為修訂哈希? 因為我們需要在我們破碎的伙伴的父提交處開始我們的交互式 Rebase 會話。

其次, --autosquash選項有什麼用? 這需要我們肩上的大量工作! 在現在打開的編輯器窗口中,一切都已經為我們準備好了:

交互式 Rebase 會話窗口
交互式 Rebase 會話窗口(大預覽)

多虧了--autosquash選項,Git 已經為我們完成了繁重的工作:

  1. 它用fixup action 關鍵字標記了我們的小創可貼提交。 這樣,Git 會將它與上面的提交直接結合,然後丟棄它。
  2. 它還相應地重新排序了行,將我們的創可貼提交直接移動到我們想要修復的提交下方(再次: fixup通過將標記的提交與上面的提交結合起來工作!)。

簡而言之:除了關上窗戶,我們無事可做!

讓我們來看看最終的結果。

  • 以前損壞的提交已修復:它現在包含我們在創可貼提交中準備的更改。
  • 醜陋的創可貼提交本身已被丟棄:提交歷史清晰且易於閱讀——就好像根本沒有發生任何錯誤一樣。
一個乾淨的提交歷史的例子
使用修復工具後的最終結果:乾淨的提交歷史! (大預覽)

知道如何消除錯誤是一種超能力

恭喜! 你現在可以在許多困難的情況下挽救你的脖子了! 我們無法真正避免這些情況:無論我們作為開發人員的經驗如何,錯誤只是工作的一部分。 但既然您知道如何應對它們,您就可以用悠閒的心率來面對它們。

如果你想了解更多關於使用 Git 撤消錯誤的信息,我可以推薦免費的“Git 急救包”,這是一系列關於這個主題的短視頻。

享受犯錯的樂趣——當然,輕鬆地撤消它們!