# 团队 Git 协作完整规范

> 适用对象：个人项目、小项目、大项目
> 版本：v1.0  ·  2026-05-07

---

## 一、核心原则

不论团队规模大小，所有 Git 协作都遵守以下 5 条铁律：

1. **主干（main）永远可发布**
2. **凡是会影响他人的操作，事前通知、事中可见、事后可恢复**
3. **分支属于"任务"，不属于"人"**（详见第六章）
4. **推送前先同步**：`git pull --rebase` 后再 `git push`
5. **重要分支禁止直接 push，必须走 PR**

---

## 二、如何选择工作流

### 决策树

```
你一个人做这个项目吗？
├─ 是 → 【方案 A】个人极简流
└─ 否 → 团队 ≥ 6 人 或 同时维护多版本？
        ├─ 是 → 【方案 C】Git Flow Lite（带 develop）
        └─ 否 → 【方案 B】GitHub Flow（90% 小项目适用）
```

### 三种方案对比

| 维度 | A·个人 | B·小项目 | C·大项目 |
|------|--------|---------|---------|
| 团队规模 | 1 人 | 2-5 人 | 6+ 人 |
| 主分支 | main | main | main + develop |
| 是否需 develop | ❌ | ❌ | ✅ |
| 是否需 release 分支 | ❌ | ❌ | ✅（可选） |
| 是否需 hotfix 分支 | ❌ | ✅ | ✅ |
| 部署策略 | 手动 / tag | 持续部署 | 阶段部署 |
| 发布周期 | 不固定 | 随时 | 双周 / 月度 |
| PR Review | 自看 | ≥1 人 | ≥2 人 |
| 复杂度 | ★ | ★★ | ★★★★ |

### 升级信号

**A → B**：第二个人加入，立刻配 main 保护 + PR 流程。

**B → C**：满足以下任一即升级
- 同时维护多个版本（v1 还在用，v2 在开发）
- 有专门 QA 阶段，main 不能合并即上线
- 发布周期 ≥ 2 周
- 团队 ≥ 6 人

> ⚠️ **宁可滞后升级，也不要预防性复杂化**。多数团队过度设计，把简单事搞复杂。

---

## 三、方案 A：个人项目工作流

### 分支模型

```
main (主线 + 发布锚点)
└─ feature/* (按需，仅用于实验性大改动)
```

### 日常工作流

**小改动 → 直接上 main**

```bash
git add .
git commit -m "fix: 修复登录按钮样式"
git push
```

**大改动 / 不确定的实验 → 开分支保命**

```bash
git checkout -b feature/重构数据层
# ...开发...

# 没问题 → 合并
git checkout main
git merge --no-ff feature/重构数据层
git push
git branch -d feature/重构数据层

# 实验失败 → 直接丢弃
git checkout main
git branch -D feature/重构数据层
```

### 发布流程

```bash
git tag -a v1.2.0 -m "release: v1.2.0 - 评查详情页上线"
git push --tags
```

**版本号遵循 [SemVer](https://semver.org/)**：
- `v主版本.次版本.补丁`
- 主版本：不兼容改动
- 次版本：新功能（向下兼容）
- 补丁：bug 修复

### 个人项目最低纪律

| 习惯 | 价值 |
|------|------|
| 每天 push 到远程 | 硬盘坏了不丢工 |
| commit 信息写人话 | 半年后能看懂 |
| 重大改动前打 tag | 有回滚锚点 |
| README 记录关键决策 | 时间久了不抓瞎 |
| `.gitignore` 配齐 | 不把密钥推上去 |

### 不要做的事

- ❌ `git commit -m "update"` / `"修改"` / `"123"`
- ❌ 一周不 push
- ❌ 在 main 上做不确定的实验
- ❌ 把 `.env` 推到远程

---

## 四、方案 B：小项目工作流（GitHub Flow）

**适用：2-5 人，持续部署**

### 分支模型

```
main (受保护，PR 合并，自动部署到生产)
├─ feature/评查详情页
├─ fix/登录闪退
└─ hotfix/支付失败  (紧急修生产)
```

**核心思想**：
- main 永远可发布
- feature 分支**生命周期短**（1-3 天最佳，最长 1 周）
- 所有改动走 PR

### 日常工作流（每个任务都按这个跑）

```bash
# 1. 开分支前先同步 main
git checkout main
git pull --rebase

# 2. 创建任务分支（任务命名，不要带人名）
git checkout -b feature/评查详情页

# 3. 开发，频繁小步提交，频繁推送
git add .
git commit -m "feat(评查): 新增详情页骨架"
git push -u origin feature/评查详情页

# 4. 推送前同步 main，避免冲突堆积
git fetch origin
git rebase origin/main
# 解决冲突 → 本地测试通过

# 5. 推送（rebase 后需要 force-with-lease）
git push --force-with-lease

# 6. 提 PR → Review → 合并 → 自动部署

# 7. 合并后清理本地
git checkout main
git pull
git branch -d feature/评查详情页
```

### PR 流程规范

**提 PR 时**：
- 标题：`<type>(<scope>): <简短描述>`，例：`feat(评查): 新增详情页`
- 描述模板：
  ```markdown
  ## 改动内容
  - ...
  
  ## 测试方法
  - ...
  
  ## 截图（如有 UI 改动）
  
  ## 关联 Issue
  Closes #123
  ```
- 自检：lint 通过、测试通过、不含调试代码

**Review 时**：
- 24 小时内响应
- 给具体可操作的反馈，不要"建议优化一下"
- Approve 前自己心里跑一遍代码逻辑

### 紧急修复（hotfix）

```bash
git checkout main && git pull
git checkout -b hotfix/支付失败
# 修 → 测 → push → PR → 紧急合并 → 立即部署
```

### 仓库一次性配置（落地关键）

**main 分支保护（GitHub/GitLab 后台）**：
- ✅ 禁止直接 push
- ✅ 禁止 force push
- ✅ 必须 PR + ≥1 Approve
- ✅ 必须通过 CI（lint + test）
- ✅ 合并后自动删除分支

---

## 五、方案 C：大项目工作流（Git Flow Lite）

**适用：6+ 人 / 多版本并存 / 有 QA 测试期**

### 分支模型

```
main      (生产版本，受保护，只接受来自 release/hotfix 的合并)
└─ develop (开发主线，受保护，所有 feature 合并到这里)
    ├─ feature/评查详情页    (个人功能分支)
    ├─ feature/登录优化
    ├─ release/v2.3.0       (准备发布的快照分支)
    └─ hotfix/支付失败       (从 main 拉，修完合并回 main + develop)
```

### 各分支角色

| 分支 | 来源 | 合并去向 | 生命周期 |
|------|------|---------|---------|
| `main` | 永久 | — | 永久 |
| `develop` | 永久 | — | 永久 |
| `feature/*` | develop | develop | 任务期 |
| `release/*` | develop | main + develop | 发布周期 |
| `hotfix/*` | main | main + develop | 紧急修复 |

### develop 的角色

- **开发主线**：所有 feature 合并到 develop
- **集成测试环境**：QA 在 develop 上测试
- **不直接发布**：发布前从 develop 拉 release 分支封板

### release 分支：发布前的"封板"

```bash
# 1. 从 develop 拉 release 分支（功能冻结）
git checkout develop && git pull
git checkout -b release/v2.3.0

# 2. 在 release 上只修 bug，不加新功能
# QA 测试 → 修 bug → 改版本号 → 写 changelog

# 3. 测试通过，合并到 main 并打 tag
git checkout main && git pull
git merge --no-ff release/v2.3.0
git tag -a v2.3.0 -m "release: v2.3.0"
git push --tags

# 4. 合并回 develop（带上 release 上的 bug 修复）
git checkout develop
git merge --no-ff release/v2.3.0
git push

# 5. 删除 release 分支
git branch -d release/v2.3.0
```

### hotfix 分支：紧急修生产

```bash
# 1. 从 main 拉 hotfix 分支
git checkout main && git pull
git checkout -b hotfix/支付失败

# 2. 修 → 测 → 提 PR

# 3. 合并到 main 并打 patch tag
git checkout main
git merge --no-ff hotfix/支付失败
git tag -a v2.3.1 -m "hotfix: 修复支付失败"
git push --tags

# 4. 必须合并回 develop！否则下次发布 bug 复发
git checkout develop
git merge --no-ff hotfix/支付失败
git push

# 5. 删除 hotfix 分支
git branch -d hotfix/支付失败
```

### 日常 feature 工作流

```bash
# 从 develop（不是 main！）开分支
git checkout develop && git pull --rebase
git checkout -b feature/评查详情页

# 开发 → 推送 → PR 到 develop（不是 main！）
git push -u origin feature/评查详情页
# 提 PR：feature/评查详情页 → develop
```

### 大项目仓库配置

- **main**：禁止 push、禁止 force push、只接受 release/hotfix 合并、必须 ≥2 Approve
- **develop**：禁止 push、必须 PR + ≥1 Approve、必须通过 CI
- **CI 要求**：lint + 单元测试 + 集成测试 + 静态分析

---

## 六、为什么禁止使用个人分支【专题】

### 反模式（团队中常见错误）

```
❌ feature/评查详情页-zhangsan
❌ zhangsan-dev
❌ feature/zhangsan/xxx
❌ 张三的分支
❌ wy-dev / wy-test
```

### 5 大问题

#### 1️⃣ 强化"个人所有权"，破坏协作精神

分支带人名 → 同事潜意识"这是张三的地盘，我别动" → 协作变独行 → 出问题没人帮看。

**好的协作是任务驱动**：分支属于任务，谁有空谁接手。

#### 2️⃣ 离职、休假、轮岗时难交接

**真实场景**：
> 张三休假一周，留下 5 条 `feature/xxx-zhangsan` 分支。李四接手时是改名（影响远程）、还是新开 `feature/xxx-lisi`（造成分裂）？怎么处理都别扭。

如果分支叫 `feature/评查详情页`，李四直接 `git checkout` 接着干，**零摩擦**。

#### 3️⃣ 分支语义模糊，看名字不知道做什么

`zhangsan-dev` 这条分支在做啥？
- 评查模块？
- 登录优化？
- 性能调优？
- 还是张三的"我啥都往这塞"草稿？

**好的分支名 = 一句话任务说明**，看到 `feature/评查详情页` 立刻明白。

#### 4️⃣ 容易堆积成"杂物分支"

人名分支没有边界，张三会把"评查详情页 + 顺手优化的登录 + 试验性能改动"都塞进 `feature/zhangsan-dev`。结果：
- PR 改动巨大，没人能 Review
- 一个 bug 拖延了三个功能
- 不能单独 revert 某项改动

**任务分支天然有边界**：`feature/评查详情页` 只装评查相关改动，超出范围就该开新分支。

#### 5️⃣ 多人协作同一功能时命名混乱

**真实场景**：
> 评查详情页很大，张三李四王五一起做。分支会变成：
> - `feature/评查详情页-zhangsan`
> - `feature/评查详情页-lisi`
> - `feature/评查详情页-wangwu`
>
> 三人改动如何同步？合并顺序？谁先合谁后合？混乱。

**正确做法**：开一条父分支 `feature/评查详情页`，每人开**子任务分支**：
- `feature/评查详情页-头部`
- `feature/评查详情页-列表`
- `feature/评查详情页-筛选`

子任务都合并回父分支，最后父分支整体合到 develop。

---

### 真实案例：人名分支造成的事故

**案例 1：交接黑洞**
> 某团队张三离职前留下 `zhangsan/refactor-payment` 分支，里面有他重构支付的工作（约 60% 完成）。半年后团队想接着做，发现：分支与 main 已差异巨大，无法 rebase；分支内 commit 信息全是"WIP"，没人知道每个 commit 在做什么；最终只能放弃，**重做**。

**案例 2：紧急修复无法落地**
> 周五晚上生产支付功能挂了。值班发现修复需要改一个文件，但该文件在 `feature/支付重构-zhangsan` 分支上张三已改了一周。要么强行修生产（与张三的分支冲突会变大），要么等张三周一来。最终用了一个肮脏的 hotfix，**技术债累计**。

**案例 3：PR Review 无人能审**
> `feature/zhangsan-dev` 一个 PR 改了 2000 行，包含评查、登录、列表、性能 4 个不相关的改动。Review 同事看了一上午放弃，盖章通过。两天后线上 bug，**无法定位是哪个改动引起的**。

---

### 正确做法

#### ✅ 任务命名规范

```
feature/<任务描述>              feature/评查详情页
feature/<issue号>-<描述>        feature/PROJ-123-评查详情页
fix/<bug简述>                  fix/登录闪退
hotfix/<紧急bug>               hotfix/支付失败
refactor/<范围>                refactor/数据层
```

#### ✅ 任务归属体现在 PR/Issue Assignee

谁负责这个任务 → 在 **PR/Issue 的 Assignee** 字段标记，不要污染分支名。

#### ✅ 分支只与「任务/目标」关联

看到 `feature/评查详情页` 应当能立刻回答：
- 这条分支在做什么
- 合并目标是什么
- 何时该删除

---

### 例外：可以带人名的场景

**仅限以下短期、可丢弃的情况**：

#### 1. 个人探索/草稿分支（明确不打算合并）

```
zhangsan/试个性能方案
zhangsan/sandbox
```

用斜线作为"个人 sandbox 命名空间"前缀，与正式 feature 分支区分。

#### 2. Fork 工作流（开源项目常见）

你 fork 仓库到自己账号后，分支名随意，提 PR 时再说。这是**远程 fork 模式**，不属于"单仓库协作"场景。

#### 3. 多人并行试方案的临时区分

```
feature/评查详情页/方案A-zhangsan
feature/评查详情页/方案B-lisi
```

验证完保留赢的方案，删除全部分支，重命名为正式分支。

> **核心：这些场景都是短期、明确不污染主流分支的。**

---

## 七、通用规范

### 分支命名

```
<type>/<description>
<type>/<issue-id>-<description>
```

**type 类型**：
- `feature/` 新功能
- `fix/` bug 修复
- `hotfix/` 紧急修复（生产）
- `refactor/` 重构
- `docs/` 文档
- `test/` 测试
- `chore/` 杂项（依赖升级、配置）
- `release/` 发布分支（仅 Git Flow）

**示例**：
```
feature/评查详情页
feature/PROJ-123-评查详情页
fix/登录闪退
hotfix/v2.3-支付失败
```

**禁止**：
- ❌ 人名前缀/后缀
- ❌ 拼音缩写如 `wy-dev`
- ❌ 没有 type 前缀
- ❌ 空格、特殊字符

### 提交信息（Conventional Commits）

```
<type>(<scope>): <简短描述>

<可选详细说明>

<可选 footer>
```

**type**：
- `feat:` 新功能
- `fix:` 修 bug
- `refactor:` 重构（不改外部行为）
- `perf:` 性能优化
- `test:` 测试
- `docs:` 文档
- `style:` 格式（不改代码逻辑）
- `chore:` 杂项
- `build:` 构建相关
- `ci:` CI 相关

**示例**：
```
feat(评查): 新增详情页骨架屏

- 添加加载占位组件
- 实现进入动画
- 适配移动端

Closes #123
```

**禁止**：
- ❌ `update`、`fix`、`修改`、`123`
- ❌ 一个 commit 改 N 个不相关的事
- ❌ `WIP`（合并前应 squash 掉）

### 标签（Tag）规范

**版本标签**遵循 [SemVer](https://semver.org/)：
```
v1.2.3              正式版
v1.2.3-beta.1       beta 版
v1.2.3-rc.1         发布候选版
```

**打 tag 时机**：
- 个人项目：每次发布
- 小项目：每次部署到生产
- 大项目：release 合并到 main 时

### .gitignore 通用模板

```gitignore
# 依赖
node_modules/
__pycache__/
*.pyc
venv/
.venv/

# 环境变量（永远不要提交）
.env
.env.*
!.env.example

# 编辑器
.vscode/
.idea/
*.swp
.DS_Store

# 构建产物
dist/
build/
*.log
coverage/

# 本地配置
.cache/
*.local
```

---

## 八、共享资源协作守则

### 通用原则

> **凡是会影响他人的操作，事前通知、事中可见、事后可恢复。**

### 共享资源守则表

| 共享资源 | 守则 |
|---------|------|
| 共享开发/测试机 | 默认主分支；切换通知；用完还原；优先 `git worktree` |
| 共享数据库 | 不在共用库做破坏性改动；每人独立 schema；测试用 docker 起本地 |
| 共享 API Key / Secret | 用 secret manager；禁止贴 IM/代码/截图 |
| CI/CD pipeline | 改流水线前通知；不在大家忙时触发昂贵任务 |
| 第三方账号 | SSO + 审计；操作记录可追溯 |
| 部署环境 | 部署窗口公告；锁机制（谁部署谁举手） |

### 切换共享资源的标准动作

```
1. 群里通知："我要 [操作] [资源]，预计 [时间]"
2. 操作
3. 用完恢复默认状态
4. 群里告知："已恢复"
```

### 优先用隔离手段，而不是切换

```bash
# git worktree：在不同目录跑不同分支，主目录不动
git worktree add ../preview-xxx feature/xxx
git worktree remove ../preview-xxx

# Docker：每个分支起独立容器
docker compose -p preview-xxx up
```

### 终极方案：消除"共享"本身

- 每个 PR 自动起一个独立预览环境（Vercel / Netlify / K8s preview）
- 每人独立数据库 schema（自动迁移）
- 每人独立云资源（云厂商沙盒账号）

> **根本不需要"共享调试机"。**

---

## 九、常见问题 FAQ

### Q1：合并前应该用 merge 还是 rebase？

| 场景 | 推荐 |
|------|------|
| 个人 feature 分支同步 main | `git rebase`（历史干净） |
| 多人协作的分支 | `git merge`（不重写他人 commit） |
| 合并 PR 到 main | **Squash and merge**（main 历史干净） |
| 已推送的分支 | 谨慎使用 rebase，需 `--force-with-lease` |

### Q2：feature 分支多久合并一次？

- **理想**：1-3 天
- **可接受**：1 周
- **超过 1 周必须警惕**：分支过大 → 拆分；同步频率提高（每天 rebase main）

### Q3：commit 太多太乱怎么办？

合并到主分支前 squash 整理：
```bash
git rebase -i HEAD~5
# 把第二个开始的 pick 改成 squash 或 fixup
```

或在 PR 合并时选择 **"Squash and merge"**。

### Q4：误推了敏感信息（密钥）怎么办？

1. **立即作废**这个密钥（去后台重新生成）
2. 用 `git filter-repo` 或 BFG 清除历史
3. **强制推送**所有分支
4. **通知所有人**重新 clone

预防：
- `.gitignore` 配齐 `.env`
- 用 secret manager
- 装 `git-secrets` / `gitleaks` pre-commit 钩子

### Q5：rebase 时遇到冲突怎么办？

```bash
# 1. 解决冲突文件
# 2. git add <冲突文件>
# 3. git rebase --continue

# 实在搞不定就放弃
git rebase --abort
```

### Q6：误删了分支怎么恢复？

```bash
# 找到最后一次 commit
git reflog

# 恢复
git checkout -b feature/xxx <commit-sha>
```

### Q7：`git push` 被拒（rejected）

意味着远程分支有新 commit 你本地没有。

```bash
# 同步后再推
git pull --rebase
git push

# 如果是 rebase 后推自己的分支
git push --force-with-lease  # 注意是 --force-with-lease，不是 --force
```

> ⚠️ **永远不要对 main / develop 用 `--force`！**

### Q8：什么时候删分支？

- 合并到主干后**立即删除**远程和本地
- GitHub/GitLab 可设置"合并后自动删除"
- 本地：`git branch -d feature/xxx`

### Q9：长期废弃的分支能直接删吗？

先 `git log feature/xxx` 看是否有未合并 commit。若有：
- 询问作者是否还需要
- 或导出 patch：`git format-patch main..feature/xxx`

### Q10：Reviewer 检查清单

审视 PR 是否值得合并：

- [ ] 分支命名规范，无人名
- [ ] 改动只做一件事，与 PR 标题一致
- [ ] 提交信息规范
- [ ] 测试覆盖关键路径
- [ ] CI 全绿
- [ ] 无调试代码（console.log、注释掉的代码）
- [ ] 无敏感信息（API Key、密码）
- [ ] 文档/注释 同步更新
- [ ] 跑过本地测试

---

## 十、配套工具与一键配置

### 全员 Git 配置（每台机器跑一次）

```bash
# pull 默认 rebase（避免无意义 merge commit）
git config --global pull.rebase true

# push 自动追踪上游
git config --global push.autoSetupRemote true

# rebase 自动 stash
git config --global rebase.autoStash true

# 默认分支名
git config --global init.defaultBranch main

# 换行符处理
git config --global core.autocrlf input  # Mac/Linux
git config --global core.autocrlf true   # Windows
```

### 推荐工具

| 类型 | 工具 | 作用 |
|------|------|------|
| 提交检查 | husky + lint-staged | commit 前自动跑 lint/format |
| 提交规范 | commitlint | 强制 Conventional Commits |
| 密钥扫描 | gitleaks / git-secrets | 防止推送密钥 |
| 图形化客户端 | Fork / SourceTree / GitKraken | 新人友好 |
| CLI 增强 | gh / lazygit | 命令行高效操作 |
| CI/CD | GitHub Actions / GitLab CI | 自动测试和部署 |
| PR 预览 | Vercel / Netlify | 每 PR 一个独立环境 |

### 一键安装 husky + lint-staged + commitlint（Node 项目）

```bash
bun add -D husky lint-staged @commitlint/cli @commitlint/config-conventional

# 初始化 husky
bunx husky init

# 配置 commitlint
echo "export default { extends: ['@commitlint/config-conventional'] }" > commitlint.config.mjs

# 添加 hooks
echo "bunx commitlint --edit \$1" > .husky/commit-msg
echo "bunx lint-staged" > .husky/pre-commit
```

`package.json` 添加：

```json
{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"]
  }
}
```

---

## 附录：决策速查表

### 我该开什么类型的分支？

| 我要做的事 | 分支前缀 | 例子 |
|----------|---------|------|
| 新功能 | `feature/` | `feature/评查详情页` |
| 修 bug | `fix/` | `fix/登录闪退` |
| 紧急修生产 | `hotfix/` | `hotfix/支付失败` |
| 重构 | `refactor/` | `refactor/数据层` |
| 写文档 | `docs/` | `docs/api文档` |
| 升级依赖 | `chore/` | `chore/升级react18` |

### 我该写什么 commit type？

| 我做了什么 | type |
|----------|------|
| 写了新功能 | `feat` |
| 修 bug | `fix` |
| 重构（不改行为） | `refactor` |
| 性能优化 | `perf` |
| 改文档 | `docs` |
| 改格式（空格、缩进） | `style` |
| 加测试 | `test` |
| 杂项 | `chore` |

### 我该用哪个工作流？

```
1 人        → 方案 A（main + 偶尔 feature）
2-5 人      → 方案 B（GitHub Flow，main + feature + PR）
6+ 人 / 多版本 → 方案 C（Git Flow Lite，main + develop + ...）
```

---

## 附录：一页纸速查（打印贴墙）

```
┌─────────────────────────────────────────────────────────┐
│            团队 Git 协作 · 一页纸速查                    │
├─────────────────────────────────────────────────────────┤
│ 1. 永远从最新主干开任务分支                              │
│    git checkout main && git pull --rebase               │
│    git checkout -b feature/任务描述                      │
│                                                         │
│ 2. 分支命名 = 任务，不带人名！                           │
│    ✅ feature/评查详情页                                 │
│    ❌ feature/评查详情页-zhangsan                        │
│                                                         │
│ 3. commit 写人话                                         │
│    ✅ feat(评查): 新增详情页骨架                         │
│    ❌ update                                            │
│                                                         │
│ 4. 推送前先同步                                          │
│    git fetch && git rebase origin/main                  │
│                                                         │
│ 5. 主分支 = PR 入口，禁止直推                            │
│                                                         │
│ 6. 共享资源 = 通知 → 操作 → 还原 → 告知                 │
│                                                         │
│ 7. 永远不对 main/develop --force                         │
│                                                         │
│ 8. 合并后立刻删分支                                      │
└─────────────────────────────────────────────────────────┘
```

---

