Skip to content

Git 版本控制

Git 是分布式版本控制系统,是团队协作的基石。AI 项目中,代码、配置、模型版本都需要 Git 管理。本页涵盖分支策略、rebase vs merge、常用操作速查、pre-commit hooks,以及 AI 项目特有的 .gitignore 最佳实践、Git LFS、submodule 和 monorepo。

Shell 脚本自动化的内容见 Shell 脚本编程,CI/CD 流水线相关内容见 MLOps。

策略特点适用场景
GitFlowmain/develop/feature/release/hotfix 多分支,结构严谨大型团队、有明确发布周期
Trunk-based所有人直接向 main 提交,配合 feature flag 短分支持续部署、敏捷团队(Google/Meta 采用)
GitHub Flowmain + feature 分支,通过 PR 合并中小团队、开源项目

💡 AI 研究项目推荐 Trunk-based:实验迭代快,分支存活时间短(通常不超过 1 天),配合 PR 做 code review。

Terminal window
# merge:保留完整历史,会产生 merge commit
git checkout main
git merge feature-branch
# rebase:把当前分支的提交"嫁接"到目标分支顶端,历史线性整洁
git checkout feature-branch
git rebase main
git checkout main
git merge feature-branch # 此时是 fast-forward,无 merge commit

⚠️ 黄金法则:已推送到远程、与他人共享的分支不要 rebase,否则会改写历史导致冲突。只对自己本地的 feature 分支 rebase。

两种合并方式在提交历史上呈现不同的形态——merge 保留分支拓扑,rebase 产生线性历史:

左侧 merge 会产生一个 merge commit M,记录了两条分支线的汇合点;右侧 rebase 把 feature 分支的提交 D 重新应用到 main 顶端变成 D',历史是一条直线,更整洁但丢失了”这些提交曾在另一分支上发生”的信息。

Terminal window
# 交互式 rebase:整理最近 5 个提交(合并、改写、重排、删除)
git rebase -i HEAD~5
# 在编辑器中可以:
# pick 保留提交
# squash 合并到前一个提交
# reword 保留但修改提交信息
# drop 删除提交
# rebase 冲突解决后继续
git rebase --continue
# 放弃 rebase
git rebase --abort
操作命令
查看状态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(二分查找)
Terminal window
# 撤销已推送的提交(生成反向提交,不改写历史)
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 reflog
Terminal window
git 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 # 设置上游跟踪

在提交前自动检查代码格式、运行 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 会自动触发检查,不通过则拒绝提交:

Terminal window
pip install pre-commit
pre-commit install # 将钩子注册到 .git/hooks/
pre-commit run --all-files # 手动对全仓库运行检查

提交信息遵循 Conventional Commits 规范,便于自动生成 changelog、语义化版本:

<type>(<scope>): <description>
[可选 body]
[可选 footer]
type含义
feat新功能
fixBug 修复
docs文档变更
style代码格式(不影响逻辑)
refactor重构(非新功能、非修复)
perf性能优化
test测试相关
chore构建、依赖、工具链变更
Terminal window
git commit -m "feat(model): 添加 ViT 分类模型支持"
git commit -m "fix(dataloader): 修复多线程下数据重复问题"
git commit -m "perf(inference): 启用 torch.compile 加速推理 30%"

AI 项目有大量不应提交的大文件和临时文件。一个完善的 .gitignore 是项目健康的基础。

# ===== Python =====
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
.eggs/
# ===== 虚拟环境 =====
.venv/
venv/
env/
# ===== IDE =====
.vscode/
.idea/
*.swp
*.swo
*~
# ===== Jupyter =====
.ipynb_checkpoints/
# ===== 测试与覆盖率 =====
.pytest_cache/
.coverage
htmlcov/
.mypy_cache/
.ruff_cache/
# ===== AI 模型与数据(大文件,用 Git LFS 或外部存储)=====
*.pt
*.pth
*.ckpt
*.safetensors
*.onnx
*.h5
*.bin
*.gguf
*.safetensors
weights/
checkpoints/
models/*/ # 但保留 models/__init__.py 等
!models/*.py
# ===== 数据集 =====
data/raw/
data/processed/
*.csv
*.parquet
*.npy
*.npz
*.tfrecord
# ===== 训练输出 =====
runs/
wandb/
mlruns/
outputs/
lightning_logs/
# ===== 环境变量与密钥 =====
.env
.env.local
secrets.yaml
*.pem
*.key
# ===== 系统文件 =====
.DS_Store
Thumbs.db
Terminal window
# 为所有项目设置全局忽略规则
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global 内容
.DS_Store
.vscode/
.idea/
*.swp

💡 已被 Git 跟踪的文件,即使后来加入 .gitignore 也不会被忽略。需要 git rm --cached <file> 取消跟踪后再忽略。

模型权重文件动辄数 GB,Git 本身(基于 delta 差异)不适合管理二进制大文件——每次修改都会让仓库膨胀。Git LFS(Large File Storage)把大文件存到独立存储,仓库中只保留指针。

Terminal window
# 安装 Git LFS
sudo apt install git-lfs # Ubuntu/Debian
brew 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.pt
git commit -m "chore: 添加模型权重"
git push
Terminal window
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。

Git submodule 允许在一个 Git 仓库中嵌套另一个 Git 仓库,适合复用共享代码(如通用数据处理库、公共模型定义)。

Terminal window
# 添加子模块
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
Terminal window
# 子模块的 HEAD 默认是 detached 状态,要修改子模块需先切到分支
cd libs/ml-utils
git checkout main
# 子模块修改后,主仓库需要提交"子模块指针更新"
cd ../..
git add libs/ml-utils
git commit -m "chore: 更新 ml-utils 子模块"

⚠️ submodule 使用复杂、易出错。如果只是复用少量代码,考虑直接复制代码或提取为 pip 包。monorepo 方案(见下文)通常更简洁。

Monorepo(单仓库)将多个项目/服务放在一个 Git 仓库中管理,与 polyrepo(每个项目一个仓库)相对。Google、Meta 等大公司采用超大规模 monorepo。

  • 多服务共享代码:推理 API、数据管道、训练脚本共享模型定义和工具库
  • 统一版本管理:所有组件版本一致,避免 A 依赖 B v1.0、C 依赖 B v2.0 的冲突
  • 原子化提交:一个 PR 同时修改共享库和所有调用方
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 # 本地开发环境
工具特点
uv workspaceuv 内置 workspace 支持(2024+),极快,推荐新项目
pip + editable installpip 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 }
  1. 提交要小而频繁:一个提交解决一个问题,便于 review 和回滚。
  2. 提交信息要有意义:fix: 修复 ResNet 在 batch_size=1 时的 BN 报错 远好于 update。
  3. 不要提交大文件:模型权重、数据集用 Git LFS 或外部存储管理。
  4. 分支生命周期要短:feature 分支最好当天合并,减少 rebase 冲突。
  5. pre-commit 钩子要统一:全团队共用一份 .pre-commit-config.yaml,保证代码风格一致。
术语英文解释
分支策略branching strategy团队约定的分支创建、命名、合并规则
变基rebase将分支提交”嫁接”到另一分支顶端,保持线性历史
拉取请求pull request (PR)请求将分支合并到目标分支的协作审查机制(GitLab 称 MR)
钩子hook在特定事件(如 commit)时自动触发的脚本
大文件存储Git LFS管理大二进制文件的 Git 扩展,文件存独立存储
子模块submodule在仓库中嵌套另一个仓库的机制
单仓库monorepo多项目/服务放在同一 Git 仓库管理的策略
语义化提交conventional commitsfeat:、fix: 等结构化提交信息规范