Git進階系列 | 8. 用Reflog恢復丟失的提交
Git是最流行的程式碼版本控制系統,這一系列文章介紹了一些Git的高階使用方式,從而幫助我們可以更好的利用Git的能力。本系列一共8篇文章,這是最後一篇。原文:Using the Reflog to Restore Lost Commits [1]
“Reflog”是Git不太為人所知的特性之一,但可能非常有用。有些人把它稱為“安全網”,而我喜歡把它看作Git的“日記”,因為Git用它來記錄 HEAD
指標的每次移動(例如,每次提交、合併、rebase、cherry-pick、reset等)。Git會將操作記錄在Reflog中,使它成為一個有價值的日誌,當出現問題時,這是一個很好的起點。
在“Git進階”系列的最後一部分,我將解釋 git log
和 git reflog
之間的區別,並展示如何使用reflog來恢復已刪除的提交和已刪除的分支。
Git進階系列:
-
用Reflog恢復丟失的提交(本文)
git log
和 git reflog
有什麼區別?
在之前的文章中,我建議使用 git log
命令檢查事件並檢視提交歷史,這正是它的工作。它可以顯示當前的 HEAD
及其祖先,即父提交,下一個父提交,等等。 git log
通過遞迴列印每個提交的父節點來回溯提交歷史,這是程式碼庫的一部分,這意味著在push、fetch、pull之後這些資訊都會被複制。
另一方面, git reflog
是一個私有的、與工作空間相關的記錄。它不遍歷祖先列表。相反,它顯示一個有序列表,包含 HEAD
過去所指向的所有提交。這就是為什麼可以把它看作某種“撤銷歷史(undo history)”,就像在文書處理器、文字編輯器等中看到的那樣。
技術上來說,這個本地記錄不是程式碼庫的一部分,它與提交分開儲存。Reflog是 .git/logs/refs/heads/
中的一個檔案,用來跟蹤每個分支的本地提交。Git的日誌通常會在90天后被清理(這是預設設定),但是可以輕鬆調整Reflog的過期日期。要將過期時間更改為180天,只需輸入以下命令:
$ git config gc.reflogExpire 180.days.ago
或者可以設定Reflog永不過期:
$ git config gc.reflogExpire never
提示:記住,Git區分了程式碼庫的配置檔案( .git /config
)、每個使用者的全域性配置( $HOME/.gitconfig
)和系統全域性設定( /etc/gitconfig
)。要為使用者或系統調整Reflog的過期時間,請在上面所示的命令後面新增 --system
或 --global
引數。
好了,現在我們有了足夠的理論背景知識,接下來可以展示如何使用 git reflog
來糾正錯誤。
恢復刪除的提交
想象一下下面這個場景: 在查看了提交歷史之後,我們決定刪除最後兩次提交。勇敢的執行了一次 git reset
後,兩個提交從提交歷史中消失了……過了一會兒,我們發現犯了個錯誤,我們丟失了有價值的更改,完蛋了!
真的要從頭再來嗎?不。換句話說,保持冷靜,利用 git reflog
!
所以,讓我們嘗試把事情搞砸,在現實生活中真的犯一下錯。下圖展示了我們最初在Tower中的提交歷史:
我們想要刪除兩個提交,並將“Change headlines for about and imprint”提交(ID: 2b504bee
)作為 master
分支上的最後一個修改。我們需要做的就是將雜湊ID複製到剪貼簿,然後在命令列中使用 git reset
並輸入雜湊:
$ git reset --hard 2b504bee
瞧。提交已經消失。現在,我們假設這是一個錯誤,並檢視Reflog來恢復丟失的資料。在終端中輸入 git reflog
檢視日誌:
有沒有注意到所有條目都是按時間順序排列的,這意味著頂部是最近的(也就是最新的)提交。如果仔細看,會注意到幾分鐘前致命的 git reset
操作就在頂部。
日記似乎起作用了,這是個好訊息。因此,我們用它來撤銷最後一個操作,並在執行reset命令之前恢復狀態。與前面一樣,將雜湊ID(在這個特定示例中為 e5b19e4
)複製到剪貼簿,再次使用 git reset
,這完全有效。但在本例中,我將基於舊狀態建立一個新分支:
$ git branch happy-ending e5b19e4
再看看圖形化Git客戶端:
如你所見,已經建立了新的 happy-ending
分支,包含了之前刪除的提交。太棒了,什麼都沒丟!
接下來看看另一個示例,用Reflog來恢復整個分支。
恢復刪除的分支
下面的示例和第一個場景類似,我們要刪除一些東西,只是這一次要刪除的是整個分支。也許你的客戶或團隊領導告訴你要擺脫一個特性分支,也許你自己想要進行清理。糟糕的是,有個提交(圖中的 C3
)沒有被包含在任何其他分支中,所以肯定會丟失資料:
我們來實際執行這個操作,稍後再恢復分支:
在刪除分支 feature/login
之前,需要先切換出來。(正如截圖中所示,這是當前的 HEAD
分支,不能在Git中刪除 HEAD
分支。)所以,我們要切換分支(到 master
),然後刪除 feature/login
:
好吧,假設我們的客戶或團隊領導改變了主意,現在又需要 feature/login
分支(包括它的提交)了,怎麼辦?
看看Git的日記:
$ git reflog
776f8ca (HEAD -> master) [email protected]{0}: checkout: moving from feature/login to master
b1c249b (feature/login) [email protected]{1}: checkout: moving from master to feature/login
[...]
我們又很幸運,最後一項顯示了從 feature/login
到 master
的切換。我們嘗試返回到之前的狀態,將雜湊ID b1c249b
複製到剪貼簿,接下來,基於期望的狀態建立一個名為 feature/login
的分支:
$ git branch feature/login b1c249b
$ git branch -vv
feature/login b1c249b Change Imprint page title
* master 776f8ca Change about title and delete error page
太棒了,分支死而復生,仍然包含了我們認為已經丟失的有價值的提交:
如果使用像Tower這樣的桌面GUI,可以簡單的按下 CMD+Z
來撤銷最後一個操作,就像文字編輯器或文書處理器一樣。
保持冷靜,保證記錄
Git的Reflog可以成為真正的救星!如你所見,很容易將丟失的提交甚至整個分支從墳墓中帶回來,只需要在reflog中找到正確的雜湊ID,其他都是小菜一碟。
如果想更深入瞭解高階Git工具,可以免費檢視“Advanced Git Kit [3] ”: 這是關於分支策略、互動式Rebase、Reflog、子模組等主題的短影片集合。
本文是“Git進階”系列的最後一部分,希望你喜歡這些文章。編碼快樂!
References:[1] Using the Reflog to Restore Lost Commits: http://css-tricks.com/using-the-reflog-to-restore-lost-commits/
你好,我是俞凡,在Motorola做過研發,現在在Mavenir做技術工作,對通訊、網路、後端架構、雲原生、DevOps、CICD、區塊鏈、AI等技術始終保持著濃厚的興趣,平時喜歡閱讀、思考,相信持續學習、終身成長,歡迎一起交流學習。
微信公眾號:DeepNoMind
- END -
- SDN系統方法 | 2. 用例
- SDN系統方法 | 1. 概述
- 微服務分散式事務處理
- Git進階系列 | 8. 用Reflog恢復丟失的提交
- Git進階系列 | 7. Git中的Cherry-pick提交
- Git進階系列 | 6. 互動式Rebase
- Git進階系列 | 5. Rebase vs Merge
- Git進階系列 | 4. 合併衝突
- Git進階系列 | 3. 基於Pull Request實現更好的協作
- Git進階系列 | 2. Git中的分支策略
- Git進階系列 | 1. 建立完美的提交
- GitOps自問自答
- GitOps的12個痛點
- GitOps指南
- 架構師成長路線圖
- 五分鐘搞懂分散式流控演算法
- Twitter 架構決策
- 基於開源元件打造Kafka自治叢集
- READS: Salesforce 服務健康指標最佳實踐
- 2萬字詳解測試金字塔