git-workflow-and-versioning
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGit Workflow and Versioning
Git工作流与版本管理
Overview
概述
Git is your safety net. Treat commits as save points, branches as sandboxes, and history as documentation. With AI agents generating code at high speed, disciplined version control is the mechanism that keeps changes manageable, reviewable, and reversible.
Git是你的安全网,你可以将提交视为存档点,分支视为沙箱,提交历史视为文档。随着AI Agent高速生成代码,规范的版本控制是保障变更可管理、可评审、可回滚的核心机制。
When to Use
适用场景
Always. Every code change flows through git.
所有场景,每一次代码变更都需要通过Git流转。
Core Principles
核心原则
Trunk-Based Development (Recommended)
Trunk-Based Development(推荐实践)
Keep always deployable. Work in short-lived feature branches that merge back within 1-3 days. Long-lived development branches are hidden costs — they diverge, create merge conflicts, and delay integration. DORA research consistently shows trunk-based development correlates with high-performing engineering teams.
mainmain ──●──●──●──●──●──●──●──●──●── (always deployable)
╲ ╱ ╲ ╱
●──●─╱ ●──╱ ← short-lived feature branches (1-3 days)This is the recommended default. Teams using gitflow or long-lived branches can adapt the principles (atomic commits, small changes, descriptive messages) to their branching model — the commit discipline matters more than the specific branching strategy.
- Dev branches are costs. Every day a branch lives, it accumulates merge risk.
- Release branches are acceptable. When you need to stabilize a release while main moves forward.
- Feature flags > long branches. Prefer deploying incomplete work behind flags rather than keeping it on a branch for weeks.
始终保持分支可部署。在短生命周期的功能分支上开发,1-3天内合并回主干。长生命周期的开发分支隐含额外成本:它们会与主干偏离、产生合并冲突、延迟集成。DORA研究长期表明,Trunk-Based Development与高绩效工程团队表现高度相关。
mainmain ──●──●──●──●──●──●──●──●──●── (always deployable)
╲ ╱ ╲ ╱
●──●─╱ ●──╱ ← short-lived feature branches (1-3 days)这是推荐的默认模式。使用gitflow或长生命周期分支的团队也可以将这些原则(原子提交、小变更、描述性信息)适配到自己的分支模型中——提交规范比具体的分支策略更重要。
- 开发分支是成本,分支每多存在一天,合并风险就多积累一分。
- 发布分支是可接受的,当你需要在主干继续迭代的同时稳定发布版本时可以使用。
- feature flags > 长分支,更推荐将未完成的功能藏在开关后部署,而不是让代码在分支上停留数周。
1. Commit Early, Commit Often
1. 早提交,多提交
Each successful increment gets its own commit. Don't accumulate large uncommitted changes.
Work pattern:
Implement slice → Test → Verify → Commit → Next slice
Not this:
Implement everything → Hope it works → Giant commitCommits are save points. If the next change breaks something, you can revert to the last known-good state instantly.
每完成一个可运行的增量模块就提交一次,不要积累大量未提交的变更。
推荐工作模式:
实现小模块 → 测试 → 验证 → 提交 → 下一个模块
错误模式:
实现所有功能 → 祈祷能跑通 → 巨型提交提交就是存档点,如果下一次变更把代码搞崩了,你可以立刻回滚到上一个已知正常的状态。
2. Atomic Commits
2. 原子提交
Each commit does one logical thing:
undefined每一次提交只做一件逻辑上的事:
undefinedGood: 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)
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
undefinedgit log --oneline
x1y2z3a Add task feature, fix sidebar, update deps, refactor utils
undefined3. Descriptive Messages
3. 描述性提交信息
Commit messages explain the why, not just the what:
undefined提交信息要解释为什么这么做,而不只是做了什么:
undefinedGood: 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.
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
坏的示例:只描述了从diff就能看出来的内容
update auth.ts
**Format:**<type>: <short description>
<optional body explaining why, not what>
**Types:**
- `feat` — New feature
- `fix` — Bug fix
- `refactor` — Code change that neither fixes a bug nor adds a feature
- `test` — Adding or updating tests
- `docs` — Documentation only
- `chore` — Tooling, dependencies, configupdate auth.ts
**格式要求:**<type>: <short description>
<optional body explaining why, not what>
**类型说明:**
- `feat` — 新功能
- `fix` — 修复bug
- `refactor` — 既不修复bug也不新增功能的代码变更
- `test` — 新增或更新测试
- `docs` — 仅文档变更
- `chore` — 工具、依赖、配置相关变更4. Keep Concerns Separate
4. 关注点分离
Don't combine formatting changes with behavior changes. Don't combine refactors with features. Each type of change should be a separate commit — and ideally a separate PR:
undefined不要把格式调整和逻辑变更混在一起,不要把重构和新功能混在一起。每一类变更都应该是单独的提交,最好是单独的PR:
undefinedGood: Separate concerns
好的示例:关注点分离
git commit -m "refactor: extract validation logic to shared utility"
git commit -m "feat: add phone number validation to registration"
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"
**Separate refactoring from feature work.** A refactoring change and a feature change are two different changes — submit them separately. This makes each change easier to review, revert, and understand in history. Small cleanups (renaming a variable) can be included in a feature commit at reviewer discretion.git commit -m "refactor validation and add phone number field"
**将重构和功能开发分开**,重构变更和功能变更是两种不同的变更,请分开提交。这样每一次变更都更容易评审、回滚,也更容易在历史记录中理解。小型清理(比如重命名变量)可以在评审人同意的情况下包含在功能提交中。5. Size Your Changes
5. 控制变更大小
Target ~100 lines per commit/PR. Changes over ~1000 lines should be split. See the splitting strategies in for how to break down large changes.
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单次提交/PR的目标大小是约100行代码,超过1000行的变更应该拆分。可以参考中的拆分策略来拆解大型变更。
code-review-and-quality~100 lines → 易评审,易回滚
~300 lines → 单个逻辑变更可接受的大小
~1000 lines → 需要拆分为更小的变更Branching Strategy
分支策略
Feature Branches
功能分支
main (always deployable)
│
├── feature/task-creation ← One feature per branch
├── feature/user-settings ← Parallel work
└── fix/duplicate-tasks ← Bug fixes- Branch from (or the team's default branch)
main - Keep branches short-lived (merge within 1-3 days) — long-lived branches are hidden costs
- Delete branches after merge
- Prefer feature flags over long-lived branches for incomplete features
main (always deployable)
│
├── feature/task-creation ← 一个分支对应一个功能
├── feature/user-settings ← 并行开发
└── fix/duplicate-tasks ← Bug修复- 从(或团队的默认分支)拉取分支
main - 保持分支短生命周期(1-3天内合并)——长生命周期分支隐含额外成本
- 合并后删除分支
- 未完成的功能优先使用feature flags,而不是长生命周期分支
Branch Naming
分支命名规范
feature/<short-description> → feature/task-creation
fix/<short-description> → fix/duplicate-tasks
chore/<short-description> → chore/update-deps
refactor/<short-description> → refactor/auth-modulefeature/<short-description> → feature/task-creation
fix/<short-description> → fix/duplicate-tasks
chore/<short-description> → chore/update-deps
refactor/<short-description> → refactor/auth-moduleWorking with Worktrees
worktree使用指南
For parallel AI agent work, use git worktrees to run multiple branches simultaneously:
bash
undefined如果有多个AI Agent并行工作,可以使用git worktree同时运行多个分支:
bash
undefinedCreate a worktree for a feature branch
为功能分支创建worktree
git worktree add ../project-feature-a feature/task-creation
git worktree add ../project-feature-b feature/user-settings
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
每个worktree都是独立的目录,对应自己的分支
Agents can work in parallel without interfering
Agent可以并行工作,不会互相干扰
ls ../
project/ ← main branch
project-feature-a/ ← task-creation branch
project-feature-b/ ← user-settings branch
ls ../
project/ ← main分支
project-feature-a/ ← task-creation分支
project-feature-b/ ← user-settings分支
When done, merge and clean up
完成后合并并清理
git worktree remove ../project-feature-a
Benefits:
- Multiple agents can work on different features simultaneously
- No branch switching needed (each directory has its own branch)
- If one experiment fails, delete the worktree — nothing is lost
- Changes are isolated until explicitly mergedgit worktree remove ../project-feature-a
优势:
- 多个Agent可以同时在不同功能上工作
- 不需要切换分支(每个目录对应自己的分支)
- 如果某个实验失败,直接删除worktree即可,不会丢失任何内容
- 变更在显式合并前都是隔离的The Save Point Pattern
存档点模式
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 historyThis pattern means you never lose more than one increment of work. If an agent goes off the rails, takes you back to the last successful state.
git reset --hard HEADAgent开始工作
│
├── 完成一次变更
│ ├── 测试通过? → 提交 → 继续
│ └── 测试失败? → 回滚到上一次提交 → 排查问题
│
├── 完成下一次变更
│ ├── 测试通过? → 提交 → 继续
│ └── 测试失败? → 回滚到上一次提交 → 排查问题
│
└── 功能开发完成 → 所有提交构成清晰的历史记录这种模式下你最多只会丢失一个增量模块的工作,如果Agent跑偏了,就能带你回到上一个正常状态。
git reset --hard HEADChange Summaries
变更摘要
After any modification, provide a structured summary. This makes review easier, documents scope discipline, and surfaces unintended changes:
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.jsonThis pattern catches wrong assumptions early and gives reviewers a clear map of the change. The "DIDN'T TOUCH" section is especially important — it shows you exercised scope discipline and didn't go on an unsolicited renovation.
每次修改完成后,提供结构化的摘要。这可以简化评审、记录范围约束、暴露非预期变更:
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这种模式可以提前捕捉错误假设,给评审人提供清晰的变更地图。"未修改内容"部分尤其重要——它表明你遵守了范围约束,没有做额外的无关修改。
Pre-Commit Hygiene
提交前检查
Before every commit:
bash
undefined每次提交前:
bash
undefined1. Check what you're about to commit
1. 检查你要提交的内容
git diff --staged
git diff --staged
2. Ensure no secrets
2. 确保没有提交密钥
git diff --staged | grep -i "password|secret|api_key|token"
git diff --staged | grep -i "password|secret|api_key|token"
3. Run tests
3. 运行测试
npm test
npm test
4. Run linting
4. 运行lint检查
npm run lint
npm run lint
5. Run type checking
5. 运行类型检查
npx tsc --noEmit
Automate this with git hooks:
```json
// package.json (using lint-staged + husky)
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}npx tsc --noEmit
使用git hooks实现自动化:
```json
// package.json (使用lint-staged + husky)
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}Handling Generated Files
生成文件处理规则
- Commit generated files only if the project expects them (e.g., , Prisma migrations)
package-lock.json - Don't commit build output (,
dist/), environment files (.next/), or IDE config (.envunless shared).vscode/settings.json - Have a that covers:
.gitignore,node_modules/,dist/,.env,.env.local*.pem
- 仅当项目要求时提交生成文件(比如、Prisma迁移文件)
package-lock.json - 不要提交构建产物(、
dist/)、环境变量文件(.next/)、IDE配置(.env除非是团队共享配置).vscode/settings.json - **配置**覆盖以下内容:
.gitignore、node_modules/、dist/、.env、.env.local*.pem
Using Git for Debugging
使用Git调试
bash
undefinedbash
undefinedFind which commit introduced a bug
找出哪个提交引入了bug
git bisect start
git bisect bad HEAD
git bisect good <known-good-commit>
git bisect start
git bisect bad HEAD
git bisect good <known-good-commit>
Git checkouts midpoints; run your test at each to narrow down
Git会自动检出中间版本,每次运行测试缩小问题范围
View what changed recently
查看最近的变更
git log --oneline -20
git diff HEAD~5..HEAD -- src/
git log --oneline -20
git diff HEAD~5..HEAD -- src/
Find who last changed a specific line
查找谁最后修改了某一行代码
git blame src/services/task.ts
git blame src/services/task.ts
Search commit messages for a keyword
按关键词搜索提交信息
git log --grep="validation" --oneline
undefinedgit log --grep="validation" --oneline
undefinedCommon Rationalizations
常见误区
| Rationalization | Reality |
|---|---|
| "I'll commit when the feature is done" | One giant commit is impossible to review, debug, or revert. Commit each slice. |
| "The message doesn't matter" | Messages are documentation. Future you (and future agents) will need to understand what changed and why. |
| "I'll squash it all later" | Squashing destroys the development narrative. Prefer clean incremental commits from the start. |
| "Branches add overhead" | Short-lived branches are free and prevent conflicting work from colliding. Long-lived branches are the problem — merge within 1-3 days. |
| "I'll split this change later" | Large changes are harder to review, riskier to deploy, and harder to revert. Split before submitting, not after. |
| "I don't need a .gitignore" | Until |
| 常见借口 | 实际情况 |
|---|---|
| "等功能开发完我再提交" | 一个巨型提交根本无法评审、调试或回滚,每完成一个小模块就提交。 |
| "提交信息不重要" | 提交信息就是文档,未来的你(以及未来的Agent)需要知道做了什么变更、为什么要变更。 |
| "我之后会把所有提交压扁" | 压扁提交会销毁整个开发过程记录,最好从一开始就提交清晰的增量变更。 |
| "分支会增加 overhead" | 短生命周期分支毫无成本,还能避免冲突工作互相影响,长生命周期分支才是问题所在,请在1-3天内完成合并。 |
| "我之后再拆分这个变更" | 大型变更更难评审,部署风险更高,也更难回滚,请在提交前拆分,而不是之后。 |
| "我不需要.gitignore文件" | 等你不小心提交了带生产环境密钥的.env文件就知道需要了,立刻配置好。 |
Red Flags
风险信号
- Large uncommitted changes accumulating
- Commit messages like "fix", "update", "misc"
- Formatting changes mixed with behavior changes
- No in the project
.gitignore - Committing ,
node_modules/, or build artifacts.env - Long-lived branches that diverge significantly from main
- Force-pushing to shared branches
- 积累了大量未提交的变更
- 提交信息类似"fix"、"update"、"misc"
- 格式调整和逻辑变更混在一起
- 项目中没有文件
.gitignore - 提交了、
node_modules/或构建产物.env - 长生命周期分支与主干严重偏离
- 向共享分支强制推送
Verification
校验清单
For every commit:
- Commit does one logical thing
- Message explains the why, follows type conventions
- Tests pass before committing
- No secrets in the diff
- No formatting-only changes mixed with behavior changes
- covers standard exclusions
.gitignore
每次提交都要确认:
- 提交只做一件逻辑上的事
- 提交信息解释了变更原因,遵循类型规范
- 提交前测试全部通过
- 提交内容中没有密钥
- 没有把仅格式调整的变更和逻辑变更混在一起
- 覆盖了标准的排除项
.gitignore