bug2025.10.24
用 Git 存储公司日常经营表格踩过的坑,以及如何切换到 Git LFS 的完整实践
适用对象:经常把 Excel/CSV/图片/PPT 等二进制表格与报表放进 Git/GitLab 的团队,数据分析团队等
核心观点:Git 擅长文本代码版本化,不擅长大型二进制文件;二进制应交给 Git LFS。
用“原生 Git”管理日常经营表格,会遇到哪些问题?
下面的问题几乎是“只要把二进制表格放进 Git,就迟早会遇到”的清单。
- 1. 仓库体积迅速膨胀
Excel/图片/PDF 属于二进制,Git 的 delta 压缩效果差;每次改动都会新增大块对象。
长期存放月报/周报/导出数据,仓库从几百 MB 涨到数 GB,克隆与备份成本陡增。
- 2. 克隆/拉取(clone/fetch/pull)极慢
Git 需要把全历史的大对象都带走;新同事初始化要等很久。
CI/CD 拉一个仓库就要数分钟甚至更久,排队时间变长。
- 3. merge 冲突难解、diff 无意义
二进制文件几乎无法进行行级 diff,合并等于“谁覆盖谁”。
容易在多人同时改一份 Excel 时产生冲突,且难以审查改了什么。
- 4. 评审与审计困难
MR 中看不到“哪一行单元格怎么变了”,只能看文件整体替换。
代码审计标准(最小变更、可读性)对二进制失效。
- 5. 服务器与运维压力
GitLab/自建 Git 需要存储和传输大量 packfile,对磁盘与带宽压力大。
垃圾回收(git gc)耗时长,仓库锁定时间变多。
- 6. 备份与迁移成本高
全量备份要把历史上的所有大文件都带走;跨机房迁移费时费力。
- 7. CI/CD 不稳定
由于拉取大仓库慢,CI 容易超时;缓存命中率低,上下行带宽被吃满。
Docker 镜像构建频繁下载 Git 历史,整体流水线变慢。
- 8. 权限与合规隐患
一旦把包含敏感数据的二进制文件提交,“历史永久可见”,删除困难(需要历史重写)。
合规要求的留存与删除策略难以精细化管理。
- 9. 开发者体验差
本地磁盘被大仓库挤爆;常见操作(如 rebase)卡顿。
为了避免卡顿,团队开始“绕着用”(比如只传压缩包、只传网盘链接),协作割裂。
结论:原生 Git 并不适合大而频繁更新的二进制表格。解决思路是把“大对象的存储/传输”与“版本指针”分离,这正是 Git LFS 的设计目标。
Git LFS 是什么?它的工作原理
Git LFS(Large File Storage) 是 Git 的一个扩展,用于管理大文件(尤其是二进制)。核心思路:
指针替身:在 Git 历史里不再存放大文件本体,而是存放一个很小的 pointer 文件(几十字节)。
大文件另存:真正的大文件被上传到独立的 LFS 存储(可以是 GitLab LFS、S3、MinIO 等)。
透明取用:开发者在 clone/checkout 时,通过 LFS 的 smudge/clean 过滤器与 batch API 自动把指针替换回文件本体。
按需下载:可以配置只在需要时下载对应版本的大文件,减少网络与磁盘开销。
文件锁(File Locking):对不可合并的设计稿/Excel 支持“锁定编辑”,避免踩踏。
一句话:Git 里只保存“小指针”,大文件放到“侧仓库”,需要时再拉。
Git LFS 能带来的实际收益
仓库体积显著瘦身:Git 历史只存指针,克隆/拉取速度大幅提升。
协作更顺滑:支持锁文件;MR 体积更小;CI 初始化更快。
存储更可控:大文件集中在 LFS 后端,便于分层存储、限额与审计。
安全与合规更友好:可以针对 LFS 存储做独立的访问控制与清理策略。
在本地与 CI 环境中,如何使用 Git LFS(最小命令集, 包含测试demo)
安装与初始化
# macOS(Homebrew)
brew install git-lfs
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y git-lfs
# Windows
scoop install git-lfs
# 在当前用户全局启用 LFS 过滤器
git lfs install
选择要用 LFS 管理的文件类型(模式)
在仓库根目录执行:
# DEMO: 把常见表格/报表/图片/压缩包纳入 LFS
git lfs track "*.xlsx"
git lfs track "*.xls"
git lfs track "*.csv"
git lfs track "*.pdf"
git lfs track "*.zip"
git lfs track "*.png"
git lfs track "*.jpg"
# 把生成的 .gitattributes 加入版本控制
git add .gitattributes
git commit -m "chore: track large binary assets with Git LFS"
.gitattributes是 LFS 的“路由表”:匹配到的文件会被自动替换为指针并走 LFS 存储。
日常使用与原生 Git 基本一致
git add <files> # 大文件会自动变成 pointer 提交
git commit -m "update: 10-24 daily report"
git push
下载策略与 CI 加速(可选)
跳过自动拉取大文件,仅在需要时下载:
# 只拉指针,不自动下载大对象(显著加快 clone)
git lfs install --skip-smudge
# 取工作目录需要的 LFS 对象
git lfs pull
CI 中常见优化(以 Linux Runner 为例):
git lfs install --skip-smudge
git clone --depth=1 <repo>
# 仅当某阶段需要大文件时再显式下载
git lfs pull --include="reports/2025-10/*.xlsx"
锁文件(对 Excel 这类不可合并文件很有用):
git lfs lock reports/finance_2025-10.xlsx
# 编辑、提交、推送
git lfs unlock reports/finance_2025-10.xlsx
把“已有 GitLab 仓库”切换到 LFS 的标准步骤 (也是这次切换中团队所需要做的)
目标:不丢历史、最小停机;将历史中的大文件迁移到 LFS,并让后续提交走 LFS。
前置准备(必做)
- 在 GitLab 项目设置或实例级设置中启用 LFS(Project → Settings → General → LFS)。
在仓库里配置 track 规则
git lfs track "*.xlsx" "*.xls" "*.csv" "*.pdf" "*.zip" "*.png" "*.jpg"
git add .gitattributes
git commit -m "chore: enable LFS tracking rules"
迁移历史中的既有大文件到 LFS
使用官方推荐的
git lfs migrate import(会重写历史)。
# 备份当前仓库(强烈建议)
git clone --mirror <repo-url> repo-backup.git
# 在主仓库执行历史迁移(可按需指定包含/排除)
git lfs migrate import --include="*.xlsx,*.xls,*.csv,*.pdf,*.zip,*.png,*.jpg"
# 推送所有分支与标签(因为历史被重写,需要 --force)
git push --all --force
git push --tags --force
提示:也可以通过
--include-ref=refs/heads/main逐步迁移,降低风险。
清理遗留“胖历史”(降低仓库体积)
# 让无引用对象尽快过期
git reflog expire --expire=now --all
git gc --prune=now --aggressive
在 GitLab 侧也可触发 Housekeeping,帮助回收旧对象。gitlab.com/gitlab-org/…
通知团队与 CI 更新
所有开发者需要重新克隆(历史已重写,旧 clone 的 commit 哈希会失效)。
在 CI 中增加 git lfs install --skip-smudge 与“按需拉取”的步骤。
文档中明确:后续二进制一律走 LFS;不可把报表打进代码层目录。
启用文件锁流程(强烈建议)
在数据团队的协作规范里加入:“编辑 Excel 前先 git lfs lock,编辑完成 git lfs unlock”。
最佳实践与落地建议
- 1. 精细化 track 规则
只跟踪真正需要版本化的二进制类型;避免把临时产物(如 .tmp、缓存)纳入 LFS。
报表建议放在固定目录(如 reports/),辅以 .gitignore 管理噪音。
- 2. 与“制品仓库/对象存储”分工
大体量、可再生产的导出数据、压缩包,优先放对象存储或制品库(Artifact/Package Registry)。
Git LFS 只保存协作必需、且确实需要版本化的文件。
- 3. 控制大小与保留策略
通过 GitLab/LFS 后端设置大小与配额阈值,避免再次“胖起来”。
为旧期报表设定归档/冷存策略(如转 S3 Glacier)。
- 4. CI 优化
默认 --skip-smudge,按需 git lfs pull;为 LFS 增加缓存(Runner 的持久卷)。
分阶段流水线:只有分析/导出阶段需要拉 LFS 对象。
- 5. 安全与合规
对敏感数据启用更严格的 LFS 存储访问策略;必要时加密存储。
避免把个人隐私或客户机密直接塞进版本库;使用脱敏数据或受控访问。
- 6. 迁移治理
先做小范围试点(如一个项目组),跑通迁移脚本与 CI 流程,再全量推广。
为“未迁移前的历史”保留镜像备份,确保回滚路径。
七、常见问题(FAQ)
Q1:迁移会不会丢历史?
A:git lfs migrate import 会重写历史(commit 哈希变化),但不会丢内容;务必提前备份并与团队约定冻结期。
Q2:历史很大,迁移很慢怎么办?
A:可以分支/时间分批迁移;或只迁移主干与关键版本,其他老分支做归档。CI/CD 与新开发优先瘦身。
Q3:GitLab 侧需要额外配置吗?
A:需要启用 LFS,并规划 LFS 存储后端与配额(GitLab.com 有带宽/存储限制;自建可接 S3/MinIO)。
Q4:为什么我 clone 后没看到大文件本体?
A:可能启用了 --skip-smudge。执行 git lfs pull 或针对目录/通配符按需拉取即可。
Q5:Excel 同时被两个人改怎么办?
A:启用 git lfs lock;不可合并文件用“锁—编辑—解锁”的串行协作方式。
总结执行过程(落地即用)
- 1. 开关与资源
- • GitLab 项目/实例启用 LFS
- • 评估 LFS 存储与带宽
- • 明确报表目录与文件类型
- 2. 规则与脚本
- •
.gitattributes:列出*.xlsx,*.csv,*.pdf,*.png,*.jpg,*.zip - •
git lfs track并提交 - • 预备
migrate脚本(可分批)
- •
- 3. 迁移与验证
- • 冻结写入、备份仓库
- •
git lfs migrate import --include="..." - • 强制推送分支与标签
- • 触发 GitLab Housekeeping
- 4. 团队成员心智培训
- • CI 加入
--skip-smudge与按需lfs pull - • 引入
git lfs lock/unlock协作规范
- • CI 加入
- 5. 后续治理
- • 定期审查 LFS 使用量与大文件分布
- • 将可再生产的大对象迁出到对象存储/制品库
- • 制定敏感数据与保留合规策略
九、示例命令(可直接拷贝)
# 全局启用 LFS
git lfs install
# 在仓库中配置追踪
git lfs track "*.xlsx" "*.xls" "*.csv" "*.pdf" "*.zip" "*.png" "*.jpg"
git add .gitattributes
git commit -m "chore: enable LFS tracking for binary assets"
# 历史迁移(建议先备份/试点)
git lfs migrate import --include="*.xlsx,*.xls,*.csv,*.pdf,*.zip,*.png,*.jpg"
# 强制推送(历史重写)
git push --all --force
git push --tags --force
# 开发者/CI 端按需拉取
git lfs install --skip-smudge
git clone --depth=1 <repo-url>
git lfs pull --include="reports/2025-10/*.xlsx"
# Excel 文件加锁协作
git lfs lock reports/finance_2025-10.xlsx
# ...编辑与提交...
git lfs unlock reports/finance_2025-10.xlsx
结语
原生 Git 管理二进制表格,长期看等同于“给仓库戴铅脚”。
Git LFS 把大文件的存储与传输从 Git 历史中剥离出来,既保留版本化能力,又显著提升性能与可维护性。
.preview-wrapper pre::before { position: absolute; top: 0; right: 0; color: #ccc; text-align: center; font-size: 0.8em; padding: 5px 10px 0; line-height: 15px; height: 15px; font-weight: 600; } .hljs.code__pre > .mac-sign { display: flex; } .code__pre { padding: 0 !important; } .hljs.code__pre code { display: -webkit-box; padding: 0.5em 1em 1em; overflow-x: auto; text-indent: 0; } h2 strong { color: inherit !important; }
本文使用 文章同步助手 同步