Git仓库管理:仓库瘦身、大文件存储与仓库迁移

说实话,我见过太多团队被Git仓库体积问题折磨得欲哭无泪。有一次,一个同事拉取代码花了整整40分钟——你猜怎么着?仓库里躺着几个几百兆的设计稿文件,还有一堆编译产物。嗯,今天我们就来聊聊怎么给Git仓库“减肥”,以及怎么处理那些不得不存的大文件。

一、仓库为什么会越来越胖?

Git仓库变胖,说白了就是两个原因:一是历史记录里塞了太多不该存的东西,二是有人把大文件直接提交了。我见过最夸张的一个项目,.git目录比源码大了50倍。

你想想看,每次提交都会生成一个快照。如果你提交了一个100MB的文件,哪怕后来删掉了,这个文件依然躺在Git的历史记录里。这就是为什么仓库会越来越臃肿。

二、仓库瘦身:git gc 与 git prune

Git自己其实有套垃圾回收机制,只是默认情况下它比较“懒”。

2.1 git gc:整理仓库碎片

git gc 的全称是 Garbage Collection。它会做几件事:

  • 把松散的对象打包成包文件(pack file)
  • 删除不可达的对象(孤儿提交)
  • 压缩和优化包文件
# 基本用法
git gc

# 激进模式,更彻底地压缩
git gc --aggressive

# 同时修剪所有分支和标签
git gc --prune=now
我的习惯:每次发布大版本前,我会跑一次 git gc --aggressive。但注意,这个操作比较耗时,别在高峰期做。

2.2 git prune:清理孤儿对象

git prune 专门用来删除那些没有被任何分支、标签引用的对象。这些对象通常是:

  • 被 rebase 丢弃的提交
  • 被 reset 撤销的提交
  • 被删除的分支上的提交
# 删除超过两周的孤儿对象
git prune

# 立即删除所有孤儿对象
git prune --expire now
注意:我曾经在项目里直接 git prune --expire now,结果发现同事还在用的一个分支被我清理了。所以,操作前一定确认没有人在用那些“孤儿”提交。

2.3 实战:给仓库彻底瘦身

如果仓库已经胖得不行了,我建议按这个步骤来:

  1. 先备份仓库(这步不能省)
  2. git reflog expire --expire=now --all 清空引用日志
  3. git gc --aggressive --prune=now 彻底压缩
  4. git count-objects -v 查看瘦身效果
# 完整瘦身流程
git reflog expire --expire=now --all
git gc --aggressive --prune=now
git count-objects -v

三、大文件存储:Git LFS

Git 本身对大文件的支持很差。为什么?因为 Git 的设计理念是存储文本差异,而二进制文件每次修改都是全新的内容。我见过一个项目,设计师把 PSD 文件直接提交,结果仓库从 200MB 涨到了 2GB。

3.1 Git LFS 的原理

Git LFS(Large File Storage)的核心思路是:用指针文件代替大文件本身。真正的文件内容存储在远程服务器上,本地只保留一个指向该文件的指针。

# 安装 Git LFS
git lfs install

# 指定哪些文件类型用 LFS 管理
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "*.mp4"

# 查看已跟踪的文件类型
git lfs track

3.2 配置 .gitattributes

执行 git lfs track 后,会自动生成或更新 .gitattributes 文件。这个文件要提交到仓库里,这样所有协作者都能用上 LFS。

# .gitattributes 示例
*.psd filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
避坑指南:我曾经犯过一个错误——在项目已经提交了大文件之后才启用 LFS。结果历史记录里依然躺着那些大文件。正确的做法是:从一开始就配置好 LFS。如果已经提交了,需要用 git lfs migrate 来迁移历史。

3.3 迁移已有的大文件

# 将历史中的大文件迁移到 LFS
git lfs migrate import --include="*.psd,*.zip" --everything

# 强制推送(需要所有协作者配合)
git push --force
警告:迁移历史会改写 Git 历史,所有协作者都需要重新克隆仓库。这个操作一定要在团队达成共识后进行。

四、仓库迁移

仓库迁移听起来简单,但坑不少。我经历过从 GitLab 迁移到 GitHub,也做过从 SVN 到 Git 的迁移。这里分享几个最实用的场景。

4.1 迁移到新的远程仓库

最常用的场景:换了代码托管平台,或者项目改名了。

# 添加新的远程仓库
git remote add new-origin https://new-server.com/team/project.git

# 推送所有分支和标签
git push new-origin --all
git push new-origin --tags

# 切换默认远程仓库
git remote remove origin
git remote rename new-origin origin

4.2 迁移部分历史

有时候你只需要迁移某个子目录的历史。比如,一个大型 monorepo 要拆分成多个小仓库。

# 使用 filter-branch 提取子目录
git filter-branch --subdirectory-filter src/utils -- --all

# 或者用 filter-repo(推荐,速度更快)
git filter-repo --path src/utils/
我的经验:git filter-branch 很慢,而且容易出错。我现在都用 git filter-repo,它是官方推荐的替代工具。安装也很简单:pip install git-filter-repo

4.3 迁移时保留提交者信息

迁移到新平台时,提交者的邮箱和用户名可能会变。我建议在迁移前统一处理:

# 修改历史中的作者信息
git filter-branch --env-filter '
OLD_EMAIL="old@email.com"
CORRECT_NAME="New Name"
CORRECT_EMAIL="new@email.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

五、知识体系总览

下面这张图概括了本章的核心内容,你可以把它当作一个快速参考:

Git 仓库管理核心 仓库瘦身 git gc 整理碎片 git prune 清理孤儿 reflog 过期清理 大文件存储 (LFS) 指针文件替代大文件 git lfs track 配置 git lfs migrate 迁移 仓库迁移 远程仓库切换 子目录提取 提交者信息修正 核心原则:保持仓库轻量、大文件用 LFS、迁移前做好备份 💡 定期执行 git gc,养成好习惯 ⚠️ 迁移前务必通知所有协作者,并做好备份

六、总结与最佳实践

好了,我们来捋一捋今天讲的核心要点:

  • 仓库瘦身:定期执行 git gc,配合 git prune 清理孤儿对象。如果仓库已经很大,用 --aggressive 模式彻底压缩。
  • 大文件存储:从一开始就用 Git LFS。如果已经提交了大文件,用 git lfs migrate 迁移历史。
  • 仓库迁移:用 git remote 切换远程仓库,用 git filter-repo 提取子目录。迁移前一定备份。
我的建议:git gc 加入你的日常习惯。我每周五下班前都会跑一次。另外,团队里最好统一一个 .gitattributes 模板,从项目初始化就配置好 LFS。这样能避免很多后期麻烦。

仓库管理这件事,说白了就是“预防胜于治疗”。前期多花几分钟配置好,后期能省下几小时甚至几天的排查时间。嗯,今天就聊到这里,希望这些经验对你有帮助。


公众号:蓝海资料掘金营,微信deep3321