跳转到内容
关系图谱

Git clean 与仓库维护详解

3,238 字 12 分钟

属性 1
status
学习中

Git clean 与仓库维护详解

git clean 用于删除工作区中未跟踪的文件git gc/git maintenance 用于仓库的垃圾回收和性能优化。前者清理工作区,后者清理 .git 目录。

一句话理解

clean 清理工作区的垃圾文件gc 清理 .git 目录的冗余数据


Part 1: git clean

基本用法

bash
# ⚠️ git clean 是不可逆操作!删除后无法恢复

# 预览将被删除的文件(干跑模式,必须先运行这个!)
git clean -n
# Would remove build/
# Would remove temp.log
# Would remove src/debug.ts

# 确认后执行删除
git clean -f
# Removing build/
# Removing temp.log
# Removing src/debug.ts

# Git 默认要求 -f(force),防止误删
# 可以通过配置改变这个行为(不推荐)
# git config clean.requireForce false

常用选项

bash
# -n / --dry-run: 预览(不实际删除)
git clean -n

# -f / --force: 强制删除
git clean -f

# -d: 同时删除未跟踪的目录
git clean -fd

# -x: 删除 .gitignore 中忽略的文件(如 node_modules、build)
git clean -fx
# ⚠️ 危险!会删除 node_modules、dist 等

# -X: 只删除 .gitignore 中忽略的文件(保留未跟踪但未被忽略的文件)
git clean -fX
# 只删除被 .gitignore 忽略的文件(清理构建缓存等)

# -i: 交互模式
git clean -di
# Would remove the following items:
#   build/    temp.log    src/debug.ts
# *** Commands ***
#   1: clean    2: filter by pattern    3: select by numbers    4: ask each one    5: quit

# -e: 排除特定模式
git clean -fd -e "*.log"
# 删除未跟踪文件和目录,但保留 .log 文件

# 组合使用
git clean -fdn    # 预览:删除未跟踪的文件和目录
git clean -fdxn   # 预览:删除所有未跟踪的(包括被忽略的)
git clean -fdXn   # 预览:只删除被 .gitignore 忽略的

选项对比

命令未跟踪文件未跟踪目录.gitignore 忽略的文件
git clean -f✅ 删除❌ 保留❌ 保留
git clean -fd✅ 删除✅ 删除❌ 保留
git clean -fx✅ 删除❌ 保留✅ 删除
git clean -fdx✅ 删除✅ 删除✅ 删除
git clean -fX❌ 保留❌ 保留✅ 删除
git clean -fdX❌ 保留✅ 删除✅ 删除

clean 实战场景

场景 1:清理构建产物

bash
# 构建缓存导致编译异常,需要完全清理重新构建

# 1. 预览
git clean -fdXn
# Would remove node_modules/
# Would remove dist/
# Would remove .turbo/
# Would remove .next/

# 2. 确认后清理
git clean -fdX

# 3. 重新安装和构建
npm install
npm run build

场景 2:彻底重置工作区

bash
# 「我想让工作区恢复到和仓库一模一样的状态」

# 1. 恢复所有已跟踪文件到最新提交
git checkout -- .
# 或
git restore .

# 2. 清理所有未跟踪文件(包括被忽略的)
git clean -fdx

# 组合命令:完全重置
git reset --hard HEAD && git clean -fdx

# ⚠️ 这会删除所有本地修改和未跟踪文件!
# 确保没有重要的未提交代码

场景 3:只清理特定目录

bash
# 只清理 src/ 目录下的未跟踪文件
git clean -fd src/

# 只清理 build/ 和 dist/
git clean -fd build/ dist/

# 排除某些文件
git clean -fd -e "*.config.js" -e "local.*"

场景 4:交互式清理

bash
git clean -fdi
# *** Commands ***
# 1: clean                - 删除所有列出的文件
# 2: filter by pattern    - 按模式过滤(如 *.log)
# 3: select by numbers    - 选择特定文件
# 4: ask each one         - 逐个确认
# 5: quit                 - 退出

# 选 4:逐个确认每个文件是否删除
# Remove temp.log [y/N]? y
# Remove debug/ [y/N]? n
# Remove test-output.txt [y/N]? y

Part 2: git gc 与仓库维护

基本概念

bash
# Git 内部存储结构
.git/
  objects/
    ab/           # 松散对象(前 2 位作为目录名)
      c1234...    # 对象文件
    pack/         # 打包文件
      pack-xxx.idx   # 索引文件
      pack-xxx.pack  # 数据文件
  refs/           # 引用(分支、标签)
  logs/           # reflog

git gc

bash
# 运行垃圾回收
git gc
# Enumerating objects: 1234, done.
# Counting objects: 100% (1234/1234), done.
# Delta compression using up to 8 threads
# Compressing objects: 100% (567/567), done.
# Writing objects: 100% (1234/1234), done.
# Total 1234 (delta 890), reused 345 (delta 234)

# 积极模式(更深度的优化)
git gc --aggressive
# 使用更多时间进行更深度的 delta 压缩
# 适合偶尔运行(如每月一次)

# 立即清理(不等待宽限期)
git gc --prune=now
# ⚠️ 慎用!会立即删除所有不可达的对象

# 自动 gc(Git 内部调用)
git gc --auto
# 只在需要时运行(松散对象超过阈值时)

gc 自动触发条件

bash
# Git 在以下情况自动运行 gc --auto:
# 1. 松散对象数超过 gc.auto(默认 6700)
# 2. packfile 数超过 gc.autoPackLimit(默认 50)

# 查看配置
git config gc.auto           # 默认 6700
git config gc.autoPackLimit  # 默认 50

# 禁用自动 gc(不推荐)
git config gc.auto 0

# 调整阈值
git config gc.auto 10000

git prune

bash
# 删除不可达的松散对象
git prune
# 通常不需要手动运行,gc 会调用

# 预览将被删除的对象
git prune --dry-run

# 指定宽限期(默认 2 周)
git prune --expire=2.weeks.ago

# 立即删除所有不可达对象
git prune --expire=now
# ⚠️ 在 reflog 也过期后才真正不可达

git maintenance(Git 2.31+)

bash
# 现代化的仓库维护工具,替代手动 gc

# 注册仓库到维护计划
git maintenance register

# 启动后台维护
git maintenance start
# 自动在后台运行以下任务:
# - prefetch: 后台预取远程数据
# - commit-graph: 更新提交图缓存
# - gc: 垃圾回收
# - loose-objects: 打包松散对象
# - incremental-repack: 增量重新打包
# - pack-refs: 打包引用

# 手动运行特定任务
git maintenance run --task=gc
git maintenance run --task=commit-graph
git maintenance run --task=loose-objects

# 停止后台维护
git maintenance stop

# 取消注册
git maintenance unregister

maintenance 的调度

bash
# maintenance 使用操作系统的调度功能
# Windows: 任务计划程序
# macOS: launchd
# Linux: cron 或 systemd timer

# 查看维护计划
git config --global --get-regexp maintenance

# 默认调度:
# hourly  — prefetch(后台预取)
# daily   — loose-objects, incremental-repack
# weekly  — pack-refs, commit-graph, gc

仓库瘦身

查看仓库大小

bash
# 查看仓库大小
git count-objects -vH
# count: 42              # 松散对象数
# size: 1.2 MiB          # 松散对象大小
# in-pack: 12345         # 打包对象数
# packs: 3               # packfile 数量
# size-pack: 156 MiB     # packfile 大小
# prune-packable: 0      # 可清理的已打包松散对象
# garbage: 0             # 垃圾文件
# size-garbage: 0 bytes

# 查看 .git 目录大小
du -sh .git
# 或 Windows PowerShell
(Get-ChildItem .git -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB

找出大文件

bash
# 找出仓库中最大的 10 个文件(当前和历史)
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  grep "^blob" | \
  sort -rnk3 | \
  head -10
# blob abc1234 52428800 assets/video.mp4
# blob def5678 15728640 data/dump.sql
# ...

# 更友好的格式
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {printf "%s\t%s\t%s\n", $3/1048576, $2, $4}' | \
  sort -rnk1 | \
  head -10
# 50.0   abc1234   assets/video.mp4
# 15.0   def5678   data/dump.sql

删除大文件

bash
# 使用 git filter-repo 删除历史中的大文件
# 安装: pip install git-filter-repo

# 删除特定文件
git filter-repo --path assets/video.mp4 --invert-paths

# 删除超过指定大小的文件
git filter-repo --strip-blobs-bigger-than 10M

# 删除后清理
git gc --aggressive --prune=now

# 强制推送(会重写历史)
git push --force --all
git push --force --tags

详见 git filter-repo

浅克隆和部分克隆

bash
# 浅克隆(只获取最近 N 个提交)
git clone --depth 1 <url>
# 适合 CI/CD 环境

# 部分克隆(不下载大文件对象)
git clone --filter=blob:none <url>
# 按需获取 blob 对象

# 部分克隆(不下载超过指定大小的文件)
git clone --filter=blob:limit=1m <url>

# 已有仓库转为浅仓库
git fetch --depth 1
git reflog expire --expire=all --all
git gc --prune=now

实战场景

场景 1:CI/CD 中的清理

bash
# CI 构建后清理工作区
git clean -fdx
# 确保每次构建都是干净的环境

# 或使用 git checkout 的 clean 选项
git checkout -- .
git clean -fdx
yaml
# GitHub Actions 中通常不需要手动 clean
# actions/checkout 每次都是全新的克隆
- uses: actions/checkout@v4
  with:
    clean: true    # 默认为 true

场景 2:仓库定期维护脚本

bash
#!/bin/bash
# repo-maintenance.sh — 仓库定期维护

echo "🔧 Git 仓库维护"
echo "================"

# 1. 查看仓库大小
echo ""
echo "📊 仓库状态:"
git count-objects -vH

# 2. 清理过期的 reflog
echo ""
echo "🗑️ 清理过期 reflog..."
git reflog expire --expire=90.days --all

# 3. 运行 gc
echo ""
echo "📦 运行 gc..."
git gc --auto

# 4. 验证仓库完整性
echo ""
echo "🔍 验证仓库完整性..."
git fsck --no-dangling

# 5. 显示结果
echo ""
echo "📊 维护后状态:"
git count-objects -vH

echo ""
echo "✅ 维护完成"

场景 3:仓库迁移前的瘦身

bash
# 将仓库从 GitHub 迁移到 GitLab 前,先瘦身

# 1. 分析大文件
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ && $3 > 1048576 {printf "%.1fMB\t%s\n", $3/1048576, $4}' | \
  sort -rnk1

# 2. 删除不需要的大文件
git filter-repo --strip-blobs-bigger-than 50M

# 3. 深度清理
git gc --aggressive --prune=now

# 4. 推送到新仓库
git remote add new-origin https://gitlab.com/team/repo.git
git push new-origin --all
git push new-origin --tags

场景 4:修复损坏的仓库

bash
# 症状:Git 操作报错 "object corrupt" 或 "bad object"

# 1. 检查仓库完整性
git fsck --full
# error: object file .git/objects/ab/c1234... is empty
# missing blob abc1234...

# 2. 如果有远程仓库,从远程恢复
git fetch origin
# Git 会自动补全缺失的对象

# 3. 如果远程也没有,尝试从 packfile 恢复
git unpack-objects < .git/objects/pack/pack-xxx.pack

# 4. 最坏情况:重新克隆
cd ..
git clone <url> project-new
# 然后将本地未提交的修改手动复制过去

场景 5:Windows 上的 clean 注意事项

powershell
# Windows 上 git clean 可能遇到的问题

# 1. 文件被占用无法删除
# 关闭 IDE、文件管理器等可能锁定文件的程序

# 2. 路径过长
# 启用长路径支持
git config --global core.longpaths true

# 3. 权限问题
# 以管理员身份运行终端

# 4. PowerShell 中的 clean 别名冲突
# PowerShell 有内置的 Clean-Item,不影响 git clean
git clean -fdn   # 正常使用

常见陷阱

陷阱 1:git clean -fdx 删除了重要文件
bash
# ❌ 不看预览直接清理
git clean -fdx
# 删除了 .env、本地配置、IDE 设置...

# ✅ 永远先预览
git clean -fdxn    # 先看会删什么
git clean -fdx     # 确认后再执行

# ✅ 或使用交互模式
git clean -fdxi    # 逐个确认
陷阱 2:gc --prune=now 导致无法恢复
bash
# ❌ 误操作后立即 gc
git reset --hard HEAD~5
git gc --prune=now    # 永久删除了那 5 个提交!

# ✅ gc 前确保 reflog 中没有需要恢复的内容
git reflog              # 先检查
git gc --prune=now      # 确认后再清理
陷阱 3:clean 不删除 .gitignore 中的文件(默认行为)
bash
# 默认 git clean -fd 不会删除 node_modules/(因为在 .gitignore 中)
git clean -fd
# node_modules/ 还在!

# 需要加 -x 才会删除被忽略的文件
git clean -fdx

# 或 -X 只删除被忽略的文件
git clean -fdX
陷阱 4:gc --aggressive 不应频繁运行
bash
# ❌ 每次提交后都运行 aggressive gc
git gc --aggressive    # 非常耗时!

# ✅ 正常使用 git gc(或让 Git 自动运行)
# 只在以下场景使用 --aggressive:
# - 仓库迁移后
# - 大规模 filter-repo 后
# - 每月/每季度一次的深度维护
陷阱 5:在子模块中运行 clean -fdx
bash
# ❌ 在主仓库运行 clean -fdx 可能清空子模块
git clean -fdx    # 子模块目录也被清空了!

# ✅ 排除子模块目录
git clean -fdx -e "libs/shared"

# ✅ 或者分别清理
git clean -fd           # 只清理未忽略的
git submodule foreach 'git clean -fd'  # 单独清理子模块

常见问题

git clean 和手动删除有什么区别?

git clean 只删除未跟踪的文件,不会误删已跟踪的文件。手动删除可能不小心删除被 Git 管理的文件。

什么时候需要手动运行 git gc

大多数情况下不需要。Git 在 fetch、merge、rebase 等操作后会自动运行 gc --auto。以下场景建议手动运行:

  • 执行了 filter-repo
  • 仓库明显变慢
  • 删除了大量分支/标签后
git maintenancegit gc 有什么区别?
特性git gcgit maintenance
引入版本一直存在Git 2.31+
运行方式手动/自动触发后台定时调度
任务范围打包+清理打包+清理+预取+提交图
适用场景简单维护大型仓库持续优化
如何查看仓库中最大的文件?
bash
# 当前工作区中最大的已跟踪文件
git ls-files -z | xargs -0 ls -la | sort -rnk5 | head -10

# 历史中最大的文件(包括已删除的)
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {printf "%.1fMB\t%s\n", $3/1048576, $4}' | \
  sort -rnk1 | head -10

速查表

git clean

目标命令
预览要删除的文件git clean -n
删除未跟踪文件git clean -f
删除未跟踪文件和目录git clean -fd
删除所有未跟踪(含忽略)git clean -fdx
只删除被忽略的文件git clean -fX
交互式清理git clean -fdi
排除特定文件git clean -fd -e "*.log"
彻底重置工作区git reset --hard && git clean -fdx

git gc / maintenance

目标命令
运行垃圾回收git gc
深度优化git gc --aggressive
立即清理不可达对象git gc --prune=now
查看仓库大小git count-objects -vH
验证仓库完整性git fsck --full
启用后台维护git maintenance start
手动运行维护任务git maintenance run --task=gc
清理过期 refloggit reflog expire --expire=90.days --all

相关命令

参考资料