Git 版本控制
Git 是分布式版本控制系统,是团队协作的基石。AI 项目中,代码、配置、模型版本都需要 Git 管理。本页涵盖分支策略、rebase vs merge、常用操作速查、pre-commit hooks,以及 AI 项目特有的 .gitignore 最佳实践、Git LFS、submodule 和 monorepo。
Shell 脚本自动化的内容见 Shell 脚本编程,CI/CD 流水线相关内容见 MLOps。
| 策略 | 特点 | 适用场景 |
|---|---|---|
| GitFlow | main/develop/feature/release/hotfix 多分支,结构严谨 | 大型团队、有明确发布周期 |
| Trunk-based | 所有人直接向 main 提交,配合 feature flag 短分支 | 持续部署、敏捷团队(Google/Meta 采用) |
| GitHub Flow | main + feature 分支,通过 PR 合并 | 中小团队、开源项目 |
💡 AI 研究项目推荐 Trunk-based:实验迭代快,分支存活时间短(通常不超过 1 天),配合 PR 做 code review。
GitFlow 分支模型
Section titled “GitFlow 分支模型”rebase vs merge
Section titled “rebase vs merge”# merge:保留完整历史,会产生 merge commitgit checkout maingit merge feature-branch
# rebase:把当前分支的提交"嫁接"到目标分支顶端,历史线性整洁git checkout feature-branchgit rebase maingit checkout maingit merge feature-branch # 此时是 fast-forward,无 merge commit⚠️ 黄金法则:已推送到远程、与他人共享的分支不要 rebase,否则会改写历史导致冲突。只对自己本地的 feature 分支 rebase。
merge 与 rebase 的历史对比
Section titled “merge 与 rebase 的历史对比”两种合并方式在提交历史上呈现不同的形态——merge 保留分支拓扑,rebase 产生线性历史:
左侧 merge 会产生一个 merge commit M,记录了两条分支线的汇合点;右侧 rebase 把 feature 分支的提交 D 重新应用到 main 顶端变成 D',历史是一条直线,更整洁但丢失了”这些提交曾在另一分支上发生”的信息。
rebase 进阶
Section titled “rebase 进阶”# 交互式 rebase:整理最近 5 个提交(合并、改写、重排、删除)git rebase -i HEAD~5# 在编辑器中可以:# pick 保留提交# squash 合并到前一个提交# reword 保留但修改提交信息# drop 删除提交
# rebase 冲突解决后继续git rebase --continue# 放弃 rebasegit rebase --abort常用操作速查表
Section titled “常用操作速查表”| 操作 | 命令 |
|---|---|
| 查看状态 | git status |
| 查看提交历史 | git log --oneline --graph --all |
| 暂存修改 | git add . 或 git add -p(交互式分块暂存) |
| 提交 | git commit -m "feat: 添加数据增强" |
| 修改最近一次提交 | git commit --amend |
| 创建并切换分支 | git checkout -b feature/new-model |
| 暂存当前工作 | git stash / 恢复 git stash pop |
| 撤销工作区修改 | git checkout -- file.py |
| 撤销已暂存的修改 | git reset HEAD file.py |
| 查看 stash 列表 | git stash list |
| 查找引入某 bug 的提交 | git bisect start(二分查找) |
# 撤销已推送的提交(生成反向提交,不改写历史)git revert <commit-hash>
# 重置到指定提交(--soft 保留修改在暂存区)git reset --soft HEAD~1# --hard 彻底丢弃修改(危险!)git reset --hard HEAD~1
# 恢复单个文件的历史版本git checkout <commit-hash> -- path/to/file.py
# 查看 reflog(所有操作记录,误操作的救命稻草)git refloggit remote -v # 查看远程仓库git remote add origin <url> # 添加远程仓库git fetch origin # 拉取远程更新(不合并)git pull --rebase origin main # 拉取并 rebase(推荐,保持线性历史)git push origin feature-branch # 推送分支git push -u origin feature-branch # 设置上游跟踪Pre-commit Hooks(husky / pre-commit)
Section titled “Pre-commit Hooks(husky / pre-commit)”在提交前自动检查代码格式、运行 lint,把问题挡在进入仓库之前。前端/Node 项目常用 husky,Python 项目常用 pre-commit:
# .pre-commit-config.yaml(Python 项目示例)repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.0 hooks: - id: ruff # 代码 lint args: [--fix] - id: ruff-format # 代码格式化
- repo: https://github.com/pre-commit/mirrors-mypy rev: v1.11.0 hooks: - id: mypy # 类型检查 additional_dependencies: [types-requests]安装后,每次 git commit 会自动触发检查,不通过则拒绝提交:
pip install pre-commitpre-commit install # 将钩子注册到 .git/hooks/pre-commit run --all-files # 手动对全仓库运行检查Conventional Commits
Section titled “Conventional Commits”提交信息遵循 Conventional Commits 规范,便于自动生成 changelog、语义化版本:
<type>(<scope>): <description>
[可选 body]
[可选 footer]| type | 含义 |
|---|---|
feat | 新功能 |
fix | Bug 修复 |
docs | 文档变更 |
style | 代码格式(不影响逻辑) |
refactor | 重构(非新功能、非修复) |
perf | 性能优化 |
test | 测试相关 |
chore | 构建、依赖、工具链变更 |
git commit -m "feat(model): 添加 ViT 分类模型支持"git commit -m "fix(dataloader): 修复多线程下数据重复问题"git commit -m "perf(inference): 启用 torch.compile 加速推理 30%".gitignore 最佳实践(AI 项目)
Section titled “.gitignore 最佳实践(AI 项目)”AI 项目有大量不应提交的大文件和临时文件。一个完善的 .gitignore 是项目健康的基础。
# ===== Python =====__pycache__/*.py[cod]*.egg-info/dist/build/.eggs/
# ===== 虚拟环境 =====.venv/venv/env/
# ===== IDE =====.vscode/.idea/*.swp*.swo*~
# ===== Jupyter =====.ipynb_checkpoints/
# ===== 测试与覆盖率 =====.pytest_cache/.coveragehtmlcov/.mypy_cache/.ruff_cache/
# ===== AI 模型与数据(大文件,用 Git LFS 或外部存储)=====*.pt*.pth*.ckpt*.safetensors*.onnx*.h5*.bin*.gguf*.safetensorsweights/checkpoints/models/*/ # 但保留 models/__init__.py 等!models/*.py
# ===== 数据集 =====data/raw/data/processed/*.csv*.parquet*.npy*.npz*.tfrecord
# ===== 训练输出 =====runs/wandb/mlruns/outputs/lightning_logs/
# ===== 环境变量与密钥 =====.env.env.localsecrets.yaml*.pem*.key
# ===== 系统文件 =====.DS_StoreThumbs.db全局 .gitignore
Section titled “全局 .gitignore”# 为所有项目设置全局忽略规则git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global 内容.DS_Store.vscode/.idea/*.swp💡 已被 Git 跟踪的文件,即使后来加入
.gitignore也不会被忽略。需要git rm --cached <file>取消跟踪后再忽略。
Git LFS(大模型文件管理)
Section titled “Git LFS(大模型文件管理)”模型权重文件动辄数 GB,Git 本身(基于 delta 差异)不适合管理二进制大文件——每次修改都会让仓库膨胀。Git LFS(Large File Storage)把大文件存到独立存储,仓库中只保留指针。
# 安装 Git LFSsudo apt install git-lfs # Ubuntu/Debianbrew install git-lfs # macOS
# 初始化(每个仓库只需一次)git lfs install
# 指定哪些文件由 LFS 管理git lfs track "*.pt"git lfs track "*.safetensors"git lfs track "models/**"
# 确认 .gitattributes 已生成(需提交到仓库)cat .gitattributes# *.pt filter=lfs diff=lfs merge=lfs -text
# 正常提交,LFS 自动处理git add model_weights.ptgit commit -m "chore: 添加模型权重"git pushLFS 查看与管理
Section titled “LFS 查看与管理”git lfs ls-files # 查看当前由 LFS 管理的文件git lfs pull # 拉取所有 LFS 文件git lfs pull --include="models/*.pt" # 只拉取特定文件
# 注意:clone 仓库时默认只拉取当前版本的 LFS 文件# 想拉取完整历史需:git lfs fetch --all⚠️ GitHub 免费账户 LFS 带宽有限(1GB/月)。大量模型文件建议用 Hugging Face Hub、内部对象存储(S3/MinIO)或专用的模型注册表管理,而非 Git LFS。
Submodule
Section titled “Submodule”Git submodule 允许在一个 Git 仓库中嵌套另一个 Git 仓库,适合复用共享代码(如通用数据处理库、公共模型定义)。
# 添加子模块git submodule add https://github.com/team/ml-utils.git libs/ml-utils
# 克隆含子模块的仓库git clone --recurse-submodules https://github.com/org/main-repo.git
# 已克隆的仓库初始化子模块git submodule update --init --recursive
# 更新所有子模块到各自最新提交git submodule update --remote --merge# 子模块的 HEAD 默认是 detached 状态,要修改子模块需先切到分支cd libs/ml-utilsgit checkout main
# 子模块修改后,主仓库需要提交"子模块指针更新"cd ../..git add libs/ml-utilsgit commit -m "chore: 更新 ml-utils 子模块"⚠️ submodule 使用复杂、易出错。如果只是复用少量代码,考虑直接复制代码或提取为 pip 包。monorepo 方案(见下文)通常更简洁。
Monorepo
Section titled “Monorepo”Monorepo(单仓库)将多个项目/服务放在一个 Git 仓库中管理,与 polyrepo(每个项目一个仓库)相对。Google、Meta 等大公司采用超大规模 monorepo。
- 多服务共享代码:推理 API、数据管道、训练脚本共享模型定义和工具库
- 统一版本管理:所有组件版本一致,避免 A 依赖 B v1.0、C 依赖 B v2.0 的冲突
- 原子化提交:一个 PR 同时修改共享库和所有调用方
目录结构示例
Section titled “目录结构示例”ai-platform/├── packages/│ ├── models/ # 共享模型定义(ResNet、ViT 等)│ ├── data-utils/ # 数据加载、预处理工具│ └── inference-core/ # 推理引擎├── services/│ ├── api/ # FastAPI 推理服务│ ├── worker/ # 异步训练 worker│ └── gateway/ # API 网关├── training/│ └── train.py # 训练脚本├── pyproject.toml # workspace 根配置└── docker-compose.yml # 本地开发环境Python monorepo 工具
Section titled “Python monorepo 工具”| 工具 | 特点 |
|---|---|
| uv workspace | uv 内置 workspace 支持(2024+),极快,推荐新项目 |
| pip + editable install | pip install -e packages/models,简单但无依赖隔离 |
| Poetry | 通过 multiple-package 模式或路径依赖支持 |
| Nx / Bazel | 通用 monorepo 构建系统(多语言),适合超大规模 |
# pyproject.toml(uv workspace 根配置)[tool.uv.workspace]members = ["packages/*", "services/*"]
[tool.uv.sources]models = { workspace = true }data-utils = { workspace = true }- 提交要小而频繁:一个提交解决一个问题,便于 review 和回滚。
- 提交信息要有意义:
fix: 修复 ResNet 在 batch_size=1 时的 BN 报错远好于update。 - 不要提交大文件:模型权重、数据集用 Git LFS 或外部存储管理。
- 分支生命周期要短:feature 分支最好当天合并,减少 rebase 冲突。
- pre-commit 钩子要统一:全团队共用一份
.pre-commit-config.yaml,保证代码风格一致。
| 术语 | 英文 | 解释 |
|---|---|---|
| 分支策略 | branching strategy | 团队约定的分支创建、命名、合并规则 |
| 变基 | rebase | 将分支提交”嫁接”到另一分支顶端,保持线性历史 |
| 拉取请求 | pull request (PR) | 请求将分支合并到目标分支的协作审查机制(GitLab 称 MR) |
| 钩子 | hook | 在特定事件(如 commit)时自动触发的脚本 |
| 大文件存储 | Git LFS | 管理大二进制文件的 Git 扩展,文件存独立存储 |
| 子模块 | submodule | 在仓库中嵌套另一个仓库的机制 |
| 单仓库 | monorepo | 多项目/服务放在同一 Git 仓库管理的策略 |
| 语义化提交 | conventional commits | feat:、fix: 等结构化提交信息规范 |
- 站内关联
- 工程基础概览 —— 本分类的入口页
- Python 环境管理 —— monorepo 的 workspace 配置
- MLOps —— 基于 Git 触发 CI/CD 流水线
- 推荐资源
- Pro Git(中文版) —— 官方免费书籍
- Conventional Commits —— 提交信息规范
- Git LFS 官方文档
- Learn Git Branching —— 可视化交互式教程