Git Filter Branch 清理指南
2025/8/9大約 4 分鐘
Git Filter-Branch 清理指南
前言
在開發過程中難免會犯錯,像是不小心推送了機密檔案、大型檔案,或是一些不該存在於版本控制中的檔案。
如果專案還很新,可以砍掉 .git 目錄,但過去的提交紀錄也都會不見。
這時候就需要用到 git filter-branch
來重寫 Git 歷史記錄。
不論是故意還是不小心幹了壞事,
完美湮滅犯案過程才是專業!!

不過使用前請三思,因為這會改變歷史記錄,如果其他人已經基於該分支工作,會需要額外的協調處理。
為什麼不能直接刪除檔案?
許多人可能會問:「為什麼不直接刪除檔案就好?」
直接刪除 vs git filter-branch 的差別:
操作方式 | 檔案狀態 | Git 歷史 | 倉庫大小 | 安全性 |
---|---|---|---|---|
直接刪除 | 目前版本消失 | 歷史仍保留 | 不變 | ❌ 敏感資料仍可存取 |
filter-branch | 完全消失 | 歷史重寫 | 減少 | ✅ 完全移除 |
具體差異:
- 倉庫大小:直接刪除大檔案後,Git 歷史中仍保留所有版本,
.git
資料夾大小不會減少 - 安全考量:即使刪除了包含密碼或 API 金鑰的檔案,任何人都可以透過
git log
和git checkout
存取舊版本 - 合規需求:某些資料保護法規要求完全移除特定資料,單純刪除無法滿足要求
- 專業形象:Git 歷史是專案的永久記錄,保持乾淨的歷史有助於維護專業形象
範例說明:
# 假設你不小心推送了 secret.txt
echo "password=123456" > secret.txt
git add secret.txt
git commit -m "Add config"
git push
# 方案 1: 直接刪除(問題方案)
rm secret.txt
git commit -m "Remove secret file"
# 結果:檔案消失了,但任何人都可以用以下指令看到密碼
git show HEAD~1:secret.txt # 仍然會顯示 "password=123456"
# 方案 2: 使用 filter-branch(正確方案)
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch secret.txt' HEAD
# 結果:檔案從歷史中完全消失,無法存取
概述
git filter-branch
是一個強大的工具,用於重寫 Git 歷史記錄。
當推送了不該存在的檔案時,可以使用此工具來清理特定分支的歷史。
基本語法
- 指定分支進行清理
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch 檔案路徑' 分支名稱
- 清理單一檔案
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch secret.txt' feature/my-branch
- 清理整個資料夾
git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch folder/' feature/my-branch
- 清理多個檔案類型
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *.log *.tmp' feature/my-branch
參數說明
--force
: 強制執行,覆蓋之前的備份--index-filter
: 在每個 commit 的索引上執行命令--cached
: 只從索引中移除,不影響工作目錄--ignore-unmatch
: 忽略檔案不存在的錯誤
完成後的操作
- 強制推送到遠端
git push origin 分支名稱 --force
- 清理本地備份
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
- 執行垃圾回收
git gc --prune=now
- 處理空 Commit 問題
使用 filter-branch
清理檔案後,某些 commit 可能變成空的(沒有實際變更)。
常見問題
Commit 有 message 但沒有檔案變更
git show --stat
顯示無變更統計
解決方法
- 方法 1: 使用 Interactive Rebase
git rebase -i HEAD~n # n 是包含空 commit 的範圍
在編輯器中:
將空 commit 行改為
drop
或直接刪除該行
方法 2: 使用 Reset (最新 commit)
git reset --hard HEAD~1
- 方法 3: 在 SourceTree 中處理
Interactive Rebase:
- 右鍵點擊空 commit 上方的 commit
- 選擇「Rebase children of [commit] interactively...」
- 將空 commit 設為「drop」或刪除該行
Reset:
- 右鍵點擊空 commit 的上一個 commit
- 選擇「Reset current branch to this commit」
- 選擇「Hard」模式
注意事項
重要警告
filter-branch
會重寫歷史記錄- 如果其他人已經基於該分支工作,需要協調處理
- 建議在操作前創建備份分支
最佳實踐
操作前備份:
git checkout -b backup-branch git checkout original-branch
通知團隊成員:
- 告知將要重寫歷史
- 確保沒有其他人正在該分支上工作
驗證結果:
git log --oneline git show --stat HEAD~3 # 檢查幾個 commit
範例場景
場景 1: 意外推送了機密檔案
# 清理 config/secrets.yml
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch config/secrets.yml' feature/user-auth
# 推送變更
git push origin feature/user-auth --force
# 清理本地
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git gc --prune=now
場景 2: 清理整個 logs 資料夾
# 清理所有 log 檔案
git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch logs/' main
# 處理可能出現的空 commit
git rebase -i HEAD~10 # 檢查最近 10 個 commit