Git 工作流与版本管理
概览
Git 是你的安全网。把 commit 当作存档点,把 branch 当作沙箱,把历史当作文档。面对高速生成代码的 AI agent,严格的版本控制纪律,是让改动保持可管理、可评审、可回滚的关键机制。
何时使用
始终如此。每一项代码改动都应该经过 git。
核心原则
基于主干的开发(Trunk-Based Development,推荐)
让 main 始终保持可部署。使用 1-3 天内就回合并的短命 feature branch。长期存在的开发分支是一种隐性成本,它们会逐渐分叉、制造冲突,并推迟集成。DORA 的研究长期显示,trunk-based development 与高绩效工程团队显著相关。
main ──●──●──●──●──●──●──●──●──●── (always deployable)
╲ ╱ ╲ ╱
●──●─╱ ●──╱ ← short-lived feature branches (1-3 days)
这是默认推荐做法。使用 gitflow 或长期分支的团队,也可以把这些原则迁移过去,例如原子提交、小改动、清晰描述。真正重要的是提交纪律,而不是某个固定分支模型。
- 开发分支本身就是成本。 分支每多活一天,就多积累一天合并风险
- 发布分支可以接受。 当你需要稳定某个版本,而
main继续前进时 - Feature flags 优于长期分支。 相比在分支上挂几周,不如把未完成功能放在 flag 后面提前部署
1. 早点提交,经常提交
每个成功的增量都应该有独立 commit,不要堆出一大坨未提交改动。
Work pattern:
Implement slice → Test → Verify → Commit → Next slice
Not this:
Implement everything → Hope it works → Giant commit
Commit 就是存档点。下一个改动如果把东西弄坏了,你能立刻回到最后一个已知正确状态。
2. 原子提交
每个 commit 只做一件逻辑上的事:
# Good: Each commit is self-contained
git log --oneline
a1b2c3d Add task creation endpoint with validation
d4e5f6g Add task creation form component
h7i8j9k Connect form to API and add loading state
m1n2o3p Add task creation tests (unit + integration)
# Bad: Everything mixed together
git log --oneline
x1y2z3a Add task feature, fix sidebar, update deps, refactor utils
3. 描述性提交信息
Commit message 要解释 why,而不只是 what:
# Good: Explains intent
feat: add email validation to registration endpoint
Prevents invalid email formats from reaching the database.
Uses Zod schema validation at the route handler level,
consistent with existing validation patterns in auth.ts.
# Bad: Describes what's obvious from the diff
update auth.ts
格式:
<type>: <short description>
<optional body explaining why, not what>
常见 type:
feat:新功能fix:bug 修复refactor:既不修 bug 也不加功能的代码改动test:补测或改测docs:纯文档改动chore:工具、依赖、配置
4. 把关注点分开
不要把格式化改动和行为改动混在一起,也不要把重构和功能混在一起。每一种类型的改动都应该是单独 commit,理想情况下也是单独 PR:
# Good: Separate concerns
git commit -m "refactor: extract validation logic to shared utility"
git commit -m "feat: add phone number validation to registration"
# Bad: Mixed concerns
git commit -m "refactor validation and add phone number field"
重构和功能要分开。 一个重构改动和一个功能改动,是两件事,应该分别提交。这样更易评审、更易回滚,历史也更容易读懂。极小的顺手清理,例如变量重命名,是否可并入功能提交,可由 reviewer 决定。
5. 控制改动规模
目标是每个 commit / PR 大约 100 行改动。超过约 1000 行就必须拆分。具体拆法见 code-review-and-quality 里的拆分策略。
~100 lines → Easy to review, easy to revert
~300 lines → Acceptable for a single logical change
~1000 lines → Split into smaller changes
分支策略
功能分支(Feature Branch)
main (always deployable)
│
├── feature/task-creation ← One feature per branch
├── feature/user-settings ← Parallel work
└── fix/duplicate-tasks ← Bug fixes
- 从
main或团队默认分支拉出 - 保持分支短命,1-3 天内合并
- 合并后删分支
- 对未完成功能优先用 feature flag,而不是长期挂分支
分支命名
feature/<short-description> → feature/task-creation
fix/<short-description> → fix/duplicate-tasks
chore/<short-description> → chore/update-deps
refactor/<short-description> → refactor/auth-module
使用工作树(Worktree)
做并行 AI agent 工作时,用 git worktree 同时挂多个分支:
# Create a worktree for a feature branch
git worktree add ../project-feature-a feature/task-creation
git worktree add ../project-feature-b feature/user-settings
# Each worktree is a separate directory with its own branch
# Agents can work in parallel without interfering
ls ../
project/ ← main branch
project-feature-a/ ← task-creation branch
project-feature-b/ ← user-settings branch
# When done, merge and clean up
git worktree remove ../project-feature-a
好处:
- 多个 agent 可以并行处理不同功能
- 不需要来回切分支,每个目录自带一个分支
- 某个实验失败,直接删 worktree 就行
- 改动在明确合并前彼此隔离
保存点模式(Save Point)
Agent starts work
│
├── Makes a change
│ ├── Test passes? → Commit → Continue
│ └── Test fails? → Revert to last commit → Investigate
│
├── Makes another change
│ ├── Test passes? → Commit → Continue
│ └── Test fails? → Revert to last commit → Investigate
│
└── Feature complete → All commits form a clean history
这种模式保证你最多只会丢失一个增量的工作。如果 agent 开始跑偏,git reset --hard HEAD 能把你拉回上一个成功状态。
变更总结
每次修改之后,都给出结构化 summary。这样便于 review,也能证明你守住了范围边界,并帮助发现意外改动:
CHANGES MADE:
- src/routes/tasks.ts: Added validation middleware to POST endpoint
- src/lib/validation.ts: Added TaskCreateSchema using Zod
THINGS I DIDN'T TOUCH (intentionally):
- src/routes/auth.ts: Has similar validation gap but out of scope
- src/middleware/error.ts: Error format could be improved (separate task)
POTENTIAL CONCERNS:
- The Zod schema is strict — rejects extra fields. Confirm this is desired.
- Added zod as a dependency (72KB gzipped) — already in package.json
这种模式能尽早暴露错误假设,也能给 reviewer 一张清晰地图。尤其是 DIDN'T TOUCH 这一段,非常重要,它证明你没有顺手把任务变成大装修。
提交前卫生
每次 commit 前都做这些检查:
# 1. Check what you're about to commit
git diff --staged
# 2. Ensure no secrets
git diff --staged | grep -i "password\|secret\|api_key\|token"
# 3. Run tests
npm test
# 4. Run linting
npm run lint
# 5. Run type checking
npx tsc --noEmit
可以配 git hooks 自动化:
// package.json (using lint-staged + husky)
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
如何处理生成文件
- 应提交生成文件,如果项目明确需要它们,例如
package-lock.json、Prisma migrations - 不要提交 构建产物,例如
dist/、.next/,环境文件例如.env,以及本地 IDE 配置,例如.vscode/settings.json,除非明确要共享 - 项目必须有
.gitignore,至少覆盖:node_modules/、dist/、.env、.env.local、*.pem
用 Git 做调试
# Find which commit introduced a bug
git bisect start
git bisect bad HEAD
git bisect good <known-good-commit>
# Git checkouts midpoints; run your test at each to narrow down
# View what changed recently
git log --oneline -20
git diff HEAD~5..HEAD -- src/
# Find who last changed a specific line
git blame src/services/task.ts
# Search commit messages for a keyword
git log --grep="validation" --oneline
常见自我安慰
| 自我安慰 | 现实 |
|---|---|
| “功能做完再一起提交” | 一个巨型提交几乎不可 review、不可调试、也不可安全回滚。每个切片都应该提交。 |
| “提交信息不重要” | 提交信息就是文档。未来的你和未来的 agent 都要靠它理解变更。 |
| “最后 squash 一下就行” | Squash 会抹掉开发过程的叙事。更好的做法,是从一开始就保持增量提交足够干净。 |
| “分支会增加负担” | 短命分支几乎没有负担,而且能避免冲突。真正麻烦的是长期分支。 |
| “这次大改先交上去,之后再拆” | 大改动更难 review、更危险、也更难回滚。应该在提交前拆,而不是提交后。 |
“不需要 .gitignore” |
直到有一天你把带生产 secrets 的 .env 提交上去了。这个东西必须第一天就配好。 |
危险信号
- 大量未提交改动不断积累
- commit message 像 “fix”、“update”、“misc” 这种没有信息量的词
- 格式化改动和行为改动混在一起
- 项目里没有
.gitignore - 把
node_modules/、.env或构建产物提交进仓库 - 长期分支和
main已严重分叉 - 对共享分支 force push
验证
对于每一个 commit,确认:
- 它只做一件逻辑上的事
- 提交信息说明了 why,并符合 type 约定
- 提交前测试通过
- diff 中没有 secrets
- 没有把纯格式化改动和行为改动混在一起
-
.gitignore覆盖了常见排除项