bug2025.10.24

81 阅读9分钟

bug2025.10.24

用 Git 存储公司日常经营表格踩过的坑,以及如何切换到 Git LFS 的完整实践

适用对象:经常把 Excel/CSV/图片/PPT 等二进制表格与报表放进 Git/GitLab 的团队,数据分析团队等
核心观点:Git 擅长文本代码版本化,不擅长大型二进制文件;二进制应交给 Git LFS。


用“原生 Git”管理日常经营表格,会遇到哪些问题?

下面的问题几乎是“只要把二进制表格放进 Git,就迟早会遇到”的清单。

  1. 1. 仓库体积迅速膨胀

Excel/图片/PDF 属于二进制,Git 的 delta 压缩效果差;每次改动都会新增大块对象。
长期存放月报/周报/导出数据,仓库从几百 MB 涨到数 GB,克隆与备份成本陡增。

  1. 2. 克隆/拉取(clone/fetch/pull)极慢

Git 需要把全历史的大对象都带走;新同事初始化要等很久。
CI/CD 拉一个仓库就要数分钟甚至更久,排队时间变长。

  1. 3. merge 冲突难解、diff 无意义

二进制文件几乎无法进行行级 diff,合并等于“谁覆盖谁”。
容易在多人同时改一份 Excel 时产生冲突,且难以审查改了什么。

  1. 4. 评审与审计困难

MR 中看不到“哪一行单元格怎么变了”,只能看文件整体替换。
代码审计标准(最小变更、可读性)对二进制失效。

  1. 5. 服务器与运维压力

GitLab/自建 Git 需要存储和传输大量 packfile,对磁盘与带宽压力大。
垃圾回收(git gc)耗时长,仓库锁定时间变多。

  1. 6. 备份与迁移成本高

全量备份要把历史上的所有大文件都带走;跨机房迁移费时费力。

  1. 7. CI/CD 不稳定

由于拉取大仓库慢,CI 容易超时;缓存命中率低,上下行带宽被吃满。
Docker 镜像构建频繁下载 Git 历史,整体流水线变慢。

  1. 8. 权限与合规隐患

一旦把包含敏感数据的二进制文件提交,“历史永久可见”,删除困难(需要历史重写)。
合规要求的留存与删除策略难以精细化管理。

  1. 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. 1. 精细化 track 规则

只跟踪真正需要版本化的二进制类型;避免把临时产物(如 .tmp、缓存)纳入 LFS。
报表建议放在固定目录(如 reports/),辅以 .gitignore 管理噪音。

  1. 2. 与“制品仓库/对象存储”分工

大体量、可再生产的导出数据、压缩包,优先放对象存储制品库(Artifact/Package Registry)
Git LFS 只保存协作必需、且确实需要版本化的文件。

  1. 3. 控制大小与保留策略

通过 GitLab/LFS 后端设置大小与配额阈值,避免再次“胖起来”。
为旧期报表设定归档/冷存策略(如转 S3 Glacier)。

  1. 4. CI 优化

默认 --skip-smudge,按需 git lfs pull;为 LFS 增加缓存(Runner 的持久卷)。
分阶段流水线:只有分析/导出阶段需要拉 LFS 对象。

  1. 5. 安全与合规

对敏感数据启用更严格的 LFS 存储访问策略;必要时加密存储。
避免把个人隐私客户机密直接塞进版本库;使用脱敏数据或受控访问。

  1. 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. 1. 开关与资源
    • • GitLab 项目/实例启用 LFS
    • • 评估 LFS 存储与带宽
    • • 明确报表目录与文件类型
  2. 2. 规则与脚本
    • .gitattributes:列出 *.xlsx,*.csv,*.pdf,*.png,*.jpg,*.zip
    • git lfs track 并提交
    • • 预备 migrate 脚本(可分批)
  3. 3. 迁移与验证
    • • 冻结写入、备份仓库
    • git lfs migrate import --include="..."
    • • 强制推送分支与标签
    • • 触发 GitLab Housekeeping
  4. 4. 团队成员心智培训
    • • CI 加入 --skip-smudge 与按需 lfs pull
    • • 引入 git lfs lock/unlock 协作规范
  5. 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; }

本文使用 文章同步助手 同步