# 团队 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 时**: - 标题:`(): <简短描述>`,例:`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/-<描述> feature/PROJ-123-评查详情页 fix/ 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 类型**: - `feature/` 新功能 - `fix/` bug 修复 - `hotfix/` 紧急修复(生产) - `refactor/` 重构 - `docs/` 文档 - `test/` 测试 - `chore/` 杂项(依赖升级、配置) - `release/` 发布分支(仅 Git Flow) **示例**: ``` feature/评查详情页 feature/PROJ-123-评查详情页 fix/登录闪退 hotfix/v2.3-支付失败 ``` **禁止**: - ❌ 人名前缀/后缀 - ❌ 拼音缩写如 `wy-dev` - ❌ 没有 type 前缀 - ❌ 空格、特殊字符 ### 提交信息(Conventional Commits) ``` (): <简短描述> <可选详细说明> <可选 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 ``` ### 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. 合并后立刻删分支 │ └─────────────────────────────────────────────────────────┘ ``` ---