刪除遠端的歷史記錄但是不影響最新的倉庫內容是筆者一直想實現的功能,有兩個很不錯的用處:
- 有的歷史提交不慎包含了比較敏感的資訊,提交的時候沒注意,過了一段時間才發現。這個時候已經有了很多新的歷史提交,無法再回退了。
- 有時候會拿Git倉庫儲存程式碼檔案以外的內容,比如美術資源、依賴庫等等。這時除了少數提交大部分歷史提交是沒意義的,還很佔倉庫空間。
不過要說明的是Git刪除歷史記錄跟我們想象中的不太一樣,需要使用的是rebase(變基)功能。這個功能對倉庫的改變還挺大的,以防外一我們還是先備份,然後建立一個分支:
git checkout -b cleanup-history
使用變基指令,重寫提交歷史,如下所示。
git rebase -i HEAD~n
-i
表示互動式重寫,會彈出一個包含所以歷史提交記錄的頁面讓你進行編輯。這裡的n表示往前回溯n個版本。例如先檢查一下所有的歷史提交:
git rev-list --count HEAD
如果得到的數值是500,那麼將n設定成499就可以看到所有的歷史記錄。有時候這個數值不對,可能是因為包含了合併的提交,那麼可以試一下:
git rev-list --first-parent --count HEAD
或者:
git rev-list --count --no-merges HEAD
來大概估一下n的數值。當然如果你回溯的歷史提交不太遠,給個大概能看到你要刪除的歷史提交即可。
在git rebase -i HEAD~n
之後在互動式頁面中,將需要刪除的歷史提交記錄的操作從pick改為drop。儲存並退出編輯器,Git會開始重寫歷史,刪除指定的提交。有時候你想刪除的歷史提交太多,一個一個改成drop很麻煩,可以使用NotePad3這樣的文字工具,透過列選取功能來批次修改。
如果你刪除的歷史記錄足夠遠足夠多,接下來你就會看到比較揪心的一幕,你的Git程式碼倉庫會回溯到最遠的歷史狀態,然後逐步開始自動提交,這個過程很可能會出現一些問題。例如檢測到空提交,會提示並中止變基過程,可以進行跳過:
git rebase --skip
還可能會遇到衝突的問題,讓你解決衝突。如果是檔案檔案,就編輯後再git add xxx
;如果是二進位制檔案,要麼刪除git rm xxx
,要麼直接git add xxx
衝突的檔案,然後繼續變基:
git rebase --continue
接下來如果一路順利,就將改動強制推送到遠端分支:
git push origin cleanup-history --force
最後,檢查一下分支的歷史提交內容,沒有問題的話將這個分支替換為主分支:
git checkout main
git reset --hard cleanup-history
git push origin main --force
如果想徹底清除這些提交記錄,並壓縮Git倉庫的體積,所以可以使用瞭如下指令:
git reflog expire --expire=now --all
git gc --prune=now --aggressive
對於其他使用者,可以使用如下指令進行更新:
git pull -rebase origin main
在筆者實際的使用過程中,遇到的衝突問題非常多,經常要停下來解決衝突的問題。筆者也不太理解為什麼刪除歷史記錄還要解決與當前倉庫快照衝突的問題,猜測可能因為筆者的歷史提交記錄包含了很多合併的提交。因此這個方法對有的讀者可能不太適用,可能合併歷史提交、或者只保留最新提交的版本更合理一點。