Git 縮減倉庫 優化 Git 倉庫大小 提高性能
隨著項目的不斷髮展,Git 倉庫可能會變得越來越大,導致克隆、推送和拉取操作變慢。本文將詳細介紹如何優化 Git 倉庫大小,提高性能和效率。
為什麼需要優化 Git 倉庫?
在以下情況下,你需要考慮優化 Git 倉庫:
- 倉庫體積過大:超過幾百 MB 甚至 GB 級別
- 克隆速度慢:新成員克隆倉庫需要很長時間
- CI/CD 效率低:持續集成構建時間過長
- 存儲成本高:託管平臺對大倉庫有限制或收費
- 歷史遺留問題:曾誤提交大文件或敏感信息
方法一:使用 git filter-repo 清理大文件(推薦)
git filter-repo 是現代 Git 倉庫清理的首選工具,比舊的 git filter-branch 更快、更安全。
安裝 git filter-repo
# 使用 pip 安裝
pip install git-filter-repo
# 或使用 Homebrew(macOS)
brew install git-filter-repo清除特定類型的文件
# 清除垃圾文件 - 大量無用的 mp3 文件
git filter-repo --path-glob '*.mp3' --invert-paths --force
# 清除所有圖片文件
git filter-repo --path-glob '*.jpg' --path-glob '*.png' --invert-paths --force
# 清除特定目錄
git filter-repo --path node_modules/ --invert-paths --force
# 清除大於 100MB 的文件
git filter-repo --strip-blobs-bigger-than 100M --force查找並刪除大文件
在實際操作前,先找出倉庫中的大文件:
# 方法1:使用 git rev-list 查找大文件
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort --numeric-sort --key=2 | \
tail -n 20 | \
cut -c 1-12,41- | \
$(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
# 方法2:使用 git ls-tree 遞歸查找
git ls-tree -r -t --long HEAD | sort -k 3 -n -r | head -20
# 方法3:查看整個歷史中的大文件
git verify-pack -v .git/objects/pack/*.idx | \
sort -k 3 -n -r | \
head -20找到大文件後,將其從歷史中徹底刪除:
# 假設發現 large-video.mp4 佔用了大量空間
git filter-repo --path large-video.mp4 --invert-paths --force
# 或者批量刪除多個文件
git filter-repo \
--path video1.mp4 \
--path video2.mp4 \
--path dataset.csv \
--invert-paths \
--force替換敏感信息
如果不小心提交了密碼、密鑰等敏感信息:
# 替換所有歷史中的敏感字符串
git filter-repo --replace-text <(echo "OLD_PASSWORD==>NEW_PASSWORD") --force
# 創建 replacements.txt 文件
cat > replacements.txt << EOF
password123==>REDACTED
api_key_abc123==>REDACTED
secret_token==>REDACTED
EOF
git filter-repo --replace-text replacements.txt --force方法二:清理標籤和分支
列出並刪除不需要的標籤
# 查看所有本地標籤
git tag -l
# 查看遠程標籤
git ls-remote --tags origin
# 刪除本地標籤
git tag -d v1.0.0
git tag -d old-release
# 刪除遠程標籤
git push origin --delete v1.0.0
git push origin --delete old-release
# 批量刪除符合模式的標籤(謹慎使用!)
git tag -l 'v0.*' | xargs git tag -d
git push origin --delete $(git tag -l 'v0.*')清理過時分支
# 查看所有本地分支
git branch -a
# 查看已合併到主分支的分支
git branch --merged main
# 查看未合併的分支
git branch --no-merged main
# 刪除已合併的本地分支
git branch -d feature/old-feature
git branch -d bugfix/fix-123
# 強制刪除未合併的分支(謹慎!)
git branch -D abandoned-feature
# 刪除遠程分支
git push origin --delete feature/old-feature
git push origin --delete bugfix/fix-123
# 清理本地對遠程已刪除分支的引用
git remote prune origin自動化清理腳本
#!/bin/bash
# cleanup-branches.sh - 安全清理過時分支
echo "=== 開始清理過時分支 ==="
# 1. 更新遠程信息
git fetch --prune
# 2. 切換到主分支
git checkout main
# 3. 列出並刪除已合併的本地分支
echo "以下分支已合併到 main,將被刪除:"
git branch --merged main | grep -v "\* main" | grep -v "develop"
read -p "確認刪除?(y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git branch --merged main | grep -v "\* main" | grep -v "develop" | xargs git branch -d
echo "本地分支清理完成"
fi
# 4. 清理遠程已刪除的分支引用
git remote prune origin
echo "=== 清理完成 ==="方法三:深度清理和垃圾回收
完整的清理流程
# 1. 清理 reflog(引用日誌)
# reflog 記錄了 HEAD 的變化歷史,會佔用空間
git reflog expire --expire=now --all
# 2. 執行激進的垃圾回收
# --aggressive 選項會更徹底地優化對象存儲
git gc --prune=now --aggressive
# 3. 重新打包對象
# -A: 將所有對象打包
# -d: 刪除多餘的包文件
git repack -Ad
# 4. 修剪懸空對象
# 刪除不再被任何引用指向的對象
git prune
# 5. 清理 stashes(如果有)
git stash clear
# 6. 驗證倉庫完整性
git fsck --full各命令詳解
git reflog expire
# 默認情況下,reflog 條目保留 90 天
# 立即過期所有 reflog 條目
git reflog expire --expire=now --all
# 只過期特定分支的 reflog
git reflog expire --expire=now refs/heads/main
# 設置不同的過期時間
git reflog expire --expire=30.days.ago --allgit gc(Garbage Collection)
# 基本垃圾回收
git gc
# 立即清理,不等待
git gc --prune=now
# 激進模式(更徹底但更慢)
git gc --aggressive
# 激進 + 立即清理(最徹底)
git gc --prune=now --aggressive
# 查看 gc 配置
git config --get gc.aggressiveDepth
git config --get gc.aggressiveWindowgc 的參數說明:
--prune=now:立即刪除不可達對象,而不是等待默認的兩星期--aggressive:更積極地優化打包,適合大倉庫--auto:僅在必要時自動運行(默認行為)
git repack
# 重新打包所有對象
git repack -Ad
# 參數說明:
# -A: 將所有 unreachable 對象也打包
# -d: 刪除舊的包文件
# -f: 強制重新打包,即使沒有變化
# -l: 僅處理本地對象
# 帶 delta 壓縮的重新打包
git repack -a -d -f --depth=250 --window=250git prune
# 刪除所有懸空對象
git prune
# 帶過期時間的修剪
git prune --expire=2.weeks.ago
# 顯示將要刪除的對象(dry run)
git prune --dry-run --verbose一鍵清理腳本
#!/bin/bash
# aggressive-cleanup.sh - 激進的倉庫清理
set -e
echo "⚠️ 警告:此操作將永久刪除歷史數據!"
echo "建議先備份倉庫或創建裸克隆作為備份"
read -p "確認繼續?(yes/no) " -r
echo
if [[ ! $REPLY == "yes" ]]; then
echo "操作已取消"
exit 1
fi
echo "📊 清理前的倉庫大小:"
du -sh .git
echo ""
echo "🔧 開始清理..."
# 1. 清理 reflog
echo " [1/6] 清理 reflog..."
git reflog expire --expire=now --all
# 2. 清理 stashes
echo " [2/6] 清理 stashes..."
git stash clear
# 3. 刪除未跟蹤的文件(可選,謹慎使用)
# echo " [3/6] 清理未跟蹤文件..."
# git clean -fdx
# 4. 垃圾回收
echo " [3/6] 執行垃圾回收..."
git gc --prune=now --aggressive
# 5. 重新打包
echo " [4/6] 重新打包對象..."
git repack -Ad
# 6. 修剪
echo " [5/6] 修剪懸空對象..."
git prune
# 7. 驗證
echo " [6/6] 驗證倉庫完整性..."
git fsck --full --no-dangling
echo ""
echo "✅ 清理完成!"
echo "📊 清理後的倉庫大小:"
du -sh .git
echo ""
echo "💡 提示:如果需要推送到遠程,請執行:"
echo " git push origin --force --all"
echo " git push origin --force --tags"方法四:推送修改後的歷史到遠程
⚠️ 重要警告: 重寫歷史後推送到遠程是破壞性操作,會影響所有協作者!
推送策略
# 1. 添加遠程倉庫(如果還沒有)
git remote add origin https://github.com/username/repository.git
# 2. 強制推送所有分支
git push origin --force --all
# 3. 強制推送所有標籤
git push origin --force --tags協作團隊的最佳實踐
如果你的項目有多個協作者,請按以下步驟操作:
# 步驟 1:通知所有團隊成員
echo "重要通知:我們將重寫 Git 歷史以優化倉庫大小。
請在今天下班前:
1. 提交併推送所有未完成的工作
2. 備份本地的重要分支
3. 明天早上重新克隆倉庫"
# 步驟 2:在維護窗口期間執行清理
# (按照前面的步驟進行清理)
# 步驟 3:強制推送
git push origin --force --all
git push origin --force --tags
# 步驟 4:通知團隊成員重新克隆
echo "清理完成!請執行以下操作:
1. 刪除本地舊倉庫
2. 重新克隆:git clone <repository-url>
3. 恢復你備份的本地分支"替代方案:創建新的乾淨倉庫
如果擔心影響協作者,可以創建新倉庫:
# 1. 克隆為裸倉庫
git clone --bare https://github.com/username/old-repo.git
cd old-repo.git
# 2. 執行清理操作
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git repack -Ad
git prune
# 3. 創建新的遠程倉庫
# 在 GitHub/GitLab 上創建新倉庫 new-repo
# 4. 推送到新倉庫
git push --mirror https://github.com/username/new-repo.git
# 5. 歸檔舊倉庫,將新倉庫設為主要倉庫方法五:預防倉庫膨脹的最佳實踐
1. 使用 .gitignore
# 完善的 .gitignore 示例
# 依賴目錄
node_modules/
vendor/
.pnp/
# 構建輸出
dist/
build/
*.exe
*.dll
*.so
*.dylib
# 日誌文件
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 環境變量
.env
.env.local
.env.*.local
# IDE 配置
.vscode/
.idea/
*.swp
*.swo
# 操作系統文件
.DS_Store
Thumbs.db
# 臨時文件
tmp/
temp/
*.tmp
*.bak
# 大型媒體文件(建議使用 Git LFS)
*.mp4
*.avi
*.mov
*.psd
*.ai2. 使用 Git LFS 管理大文件
# 安裝 Git LFS
git lfs install
# 追蹤大文件類型
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"
# 查看追蹤的文件類型
git lfs track
# 提交 .gitattributes 文件
git add .gitattributes
git commit -m "Configure Git LFS for large files"
# 推送 LFS 對象
git lfs push --all origin3. 定期維護計劃
# 添加到 crontab,每月執行一次
# 編輯 crontab
crontab -e
# 添加以下行(每月1號凌晨2點執行)
0 2 1 * * cd /path/to/repo && git gc --auto
# 或者每季度執行一次深度清理
0 2 1 */3 * * cd /path/to/repo && bash /path/to/aggressive-cleanup.sh4. 監控倉庫大小
# 創建監控腳本 monitor-size.sh
#!/bin/bash
REPO_DIR="/path/to/repo"
MAX_SIZE_MB=500
cd $REPO_DIR
# 獲取 .git 目錄大小(MB)
SIZE_MB=$(du -sm .git | cut -f1)
echo "當前倉庫大小: ${SIZE_MB}MB"
if [ $SIZE_MB -gt $MAX_SIZE_MB ]; then
echo "⚠️ 警告:倉庫大小超過 ${MAX_SIZE_MB}MB!"
echo "建議執行清理操作"
# 可以在此處添加郵件通知
# mail -s "Git Repo Size Alert" admin@example.com <<< "Size: ${SIZE_MB}MB"
else
echo "✅ 倉庫大小正常"
fi常見問題排查
問題 1:清理後倉庫大小沒有明顯減少
# 原因:可能有其他引用仍指向這些對象
# 解決方案:
# 1. 確保清理了所有引用
git reflog expire --expire=now --all
git stash clear
# 2. 檢查是否有其他遠程引用
git remote -v
git fetch --all --prune
# 3. 執行最徹底的清理
git gc --prune=now --aggressive
git repack -Ad
git prune
# 4. 驗證效果
du -sh .git問題 2:強制推送後被拒絕
# 錯誤:protected branch update failed
# 解決方案:
# 1. 在 GitHub/GitLab 上臨時解除分支保護
# 2. 執行強制推送
git push origin --force --all
# 3. 重新啟用分支保護
# 或者使用 --force-with-lease(更安全)
git push origin --force-with-lease --all問題 3:清理後出現倉庫損壞
# 檢查倉庫完整性
git fsck --full
# 如果有錯誤,從備份恢復
# 這就是為什麼要先備份的原因!
# 嘗試修復
git gc --prune=now
git reflog expire --expire=now --all問題 4:想知道哪些文件佔用最多空間
# 綜合查詢腳本
#!/bin/bash
echo "=== Top 20 Largest Files in Git History ==="
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort --numeric-sort --key=2 | \
tail -n 20 | \
awk '{printf "%-50s %s\n", $4, $3}' | \
column -t
echo ""
echo "=== Largest Directories ==="
git ls-tree -r -t --long HEAD | \
awk '{print $4}' | \
sed 's|/[^/]*$||' | \
sort | uniq -c | sort -rn | \
head -10實際案例:將一個 2GB 的倉庫縮減到 200MB
以下是一個真實案例的完整操作流程:
# 初始狀態
$ du -sh .git
2.0G .git
# 步驟 1:找出問題所在
$ git verify-pack -v .git/objects/pack/*.idx | \
sort -k 3 -n -r | head -10
# 發現幾個大文件:
# - database-dump.sql (500MB)
# - video-demo.mp4 (800MB)
# - node_modules/ (多次提交,累計 400MB)
# 步驟 2:從歷史中刪除這些文件
$ git filter-repo \
--path database-dump.sql \
--path video-demo.mp4 \
--path node_modules/ \
--invert-paths \
--force
# 步驟 3:刪除舊標籤
$ git tag -l 'v1.*' | xargs git tag -d
$ git push origin --delete $(git tag -l 'v1.*')
# 步驟 4:刪除廢棄分支
$ git branch -D old-experiment
$ git push origin --delete old-experiment
# 步驟 5:深度清理
$ git reflog expire --expire=now --all
$ git gc --prune=now --aggressive
$ git repack -Ad
$ git prune
# 最終結果
$ du -sh .git
200M .git
# 步驟 6:推送到遠程
$ git push origin --force --all
$ git push origin --force --tags
# 縮減比例:90% 🎉總結
優化 Git 倉庫大小是一個系統性的工作,需要根據具體情況選擇合適的方法:
方法對比
| 方法 | 適用場景 | 風險等級 | 效果 |
|---|---|---|---|
| git filter-repo | 刪除大文件、敏感信息 | 高(重寫歷史) | ⭐⭐⭐⭐⭐ |
| 清理標籤分支 | 刪除過時引用 | 低 | ⭐⭐ |
| 垃圾回收 | 日常維護 | 極低 | ⭐⭐⭐ |
| Git LFS | 預防大文件 | 無 | ⭐⭐⭐⭐ |
| .gitignore | 預防不必要文件 | 無 | ⭐⭐⭐⭐ |
最佳實踐建議
- 預防為主:配置好
.gitignore,使用 Git LFS - 定期維護:設置自動化清理任務
- 謹慎操作:重寫歷史前務必備份
- 團隊協作:提前溝通,選擇合適的時機
- 監控預警:設置倉庫大小監控
下一步學習
通過合理運用這些技術,你可以保持 Git 倉庫的健康狀態,提高開發效率!