程式版控不該發生,但難免會遇到的狀況 - 想移除歷史 Commit 裡不該被放進去的檔案,例如:無保存價值的大檔或是機密資料。

如果是無用大檔,即使將檔案刪除再 Commit,日後 clone 仍會佔用頻寬及儲存空間;而機密資料則是非清除不可。Git 允許我們修改歷史並 push -f 覆寫,但實務上會造成開發團隊夥伴的困擾,覆寫後版本會跟其他人先前取得的版本不一致,需要善後。(參考:Git Force Push 後如何更新 Repository by Yowko)

如果了解副作用,也有充分理由必須回到過去重寫歷史,同時也已取得夥伴諒解確認不會被蓋布袋,下面是兩個簡單練習,供未來的我及有需要的同學參考。

【情境一】

歷史有四次 Commit,想移除第二次 Commit 不小心放入的 WiFiPasswd.txt:

利用 git rebase -i 837658aa (Commit "init") Rebase 第二到四個 Commit,將 Poc Commit 標為 edit,要求在此停下修改:

存檔後,版本會停在 PoC Commit,顯示 "Stop at aacb399... Poc You can amend the commit now" 訊息,此時下指令 git rm WiFiPasswd.txt 移除檔案,再下指令 git commit --amend 修改 Commit。COMMIT_EDITMSG 檔可看到檔案清單的 WiFiPasswd.txt 已消失,只剩 Program.cs [1],此時亦可修改 Commit 訊息[2]:

之後執行 git rebase --continue,後面兩個 Commit 不需修改,Rebase 完成。再檢視 Poc Commit,其中已無 WiFiPasswd.txt:

【情境二】

有個 secrets 目錄存在多個 Commit,打算將整個資料夾從歷史抹去:

使用 filter-branch 指令,配合 index-filter --all 對所有 Commit Index 跑一次 git rm 將 secrets 目錄移除再重新 Commit。 (註:另一個做法是用 tree-filter 將 Commit 內容還原到暫存目錄修改,優點是可對實體檔案內容做精密調整,缺點是速度較慢,若為單純移除檔案,index-filter 較有效率)

git filter-branch --force --index-filter "git rm --cached --ignore-unmatch secrets -r" -- --all

由訊息可看到 secrets 的兩個檔案被從第二到第四個 Commit 移除。但要注意,刪除檔案在本機仍可被還原,要徹底清除可參考龍哥這篇 【冷知識】怎麼樣把檔案真正的從 Git 裡移掉?

註:官方推薦用 filter-repo 取代 filter-branch,效率較好且功能更多,但需額外下載安裝,先筆記,未來有需要再學。

以上是兩個從歷史抹去檔案的範例,希望大家不會用到它們。


Comments

# by Nathan

REALLY HELPFUL!

Post a comment