← 返回笔记列表

Git 分支管理在小团队里的简化用法

前一阵按教程搬了一套完整的 Git Flow,masterdevelopfeaturereleasehotfix 五类分支齐全。结果三四个人的团队,一半时间花在合并和同步分支上,出问题的次数反而变多了。后来砍成两层,反倒顺畅了。记一下现在的做法。

一、为什么砍掉 develop 分支

Git Flow 是为「有明确版本号、定期发布、需要同时维护多个已发布版本」的产品设计的。而我们是持续部署,合进主干基本当天就上线,developmain 长期只差几个提交,唯一的作用就是每次发布时多做一次合并。

更麻烦的是两条长期分支容易分叉。有人往 develop 提,有人直接改 main 上的紧急问题,回合的时候冲突一大堆。

现在只保留两层:

  • main —— 唯一的长期分支,始终保持可部署状态
  • feature/xxx、fix/xxx —— 短生命周期分支,合并后立刻删除

二、日常流程

# 1. 从最新的 main 切出分支
git checkout main
git pull --rebase origin main
git checkout -b feature/order-export

# 2. 正常开发提交
git add .
git commit -m "feat: 订单列表支持导出 Excel"

# 3. 推送并开 PR
git push -u origin feature/order-export

分支命名用 类型/简短描述,常用的就三种:feature/ 新功能、fix/ 修问题、chore/ 依赖升级或配置调整。带上任务编号也行,比如 feature/1024-order-export,方便回溯。

关键约束:一个分支只做一件事,尽量控制在两三天内合掉。分支活得越久,冲突越难解。改着改着发现顺手改了别的,就拆成两个分支。

三、同步主干用 rebase 而不是 merge

开发期间 main 有了新提交,需要同步过来:

git fetch origin
git rebase origin/main

rebase 而不是 merge origin/main,是为了让特性分支的提交历史保持线性,PR 里只看到自己的改动,不会混进一堆「Merge branch 'main' into ...」的噪音提交。

冲突了就正常解决:

git add 冲突的文件
git rebase --continue
# 实在搞不定就退回去
git rebase --abort

rebase 之后本地历史被改写,推送需要强推。务必用 --force-with-lease 而不是 -f

git push --force-with-lease

区别在于,如果远程分支在你上次 fetch 之后被别人更新过,--force-with-lease 会拒绝推送,而 -f 会直接覆盖掉同事的提交。这条规矩定下来之后就没再丢过代码。

另外一条铁律:只对自己的特性分支 rebase,永远不要 rebase 已经共享的 main。

四、Code Review 的实际做法

规则很简单:所有代码都走 PR,至少一个人 approve 才能合。在仓库设置里把 main 设为保护分支,禁止直接 push。

为了让 review 不流于形式,加了几条约定:

  • PR 控制在 400 行改动以内。超过这个量,review 的质量会断崖式下降,基本变成走过场。太大就拆。
  • PR 描述写清楚「为什么」而不是「改了什么」。改了什么看 diff 就知道,改的原因和考虑过的其他方案只有作者清楚。
  • 评论区分级别。用前缀标明:[必改] 阻塞合并、[建议] 可以不改、[疑问] 只是想确认一下。避免作者分不清哪些是硬要求。
  • CI 不过不 review。测试和 lint 交给流水线,人只看逻辑和设计。

五、合并方式选 Squash

三种合并方式的差别:

方式结果适用
Merge commit保留全部提交 + 一个合并节点需要完整开发过程记录时
Squash merge压成一个提交合入日常特性分支(我们的默认)
Rebase merge提交逐个接到主干上,无合并节点每个提交都干净且有意义时

我们默认用 Squash。开发过程中的「修复拼写」「再试一次」「补个注释」这类提交没有保留价值,压成一条之后 main 的历史是一条干净的功能列表,回滚也简单——一个 revert 就能撤掉整个功能。

合完顺手删分支,GitHub 和 GitLab 都能配置自动删除。本地的残留分支定期清理:

git fetch --prune
git branch --merged main | grep -v main | xargs -r git branch -d

六、提交信息格式

用了 Conventional Commits 的简化版,只要求前缀:

feat:     新功能
fix:      修复问题
docs:     文档
refactor: 重构,不改变外部行为
test:     测试相关
chore:    构建、依赖、配置

格式是 类型: 一句话说明,中文英文都行,统一就好。好处是 git log --oneline 一眼能扫出这个版本都改了什么,也方便自动生成变更日志。

提交信息写「做了什么」,不写「怎么做的」。fix: 修复订单金额在跨时区时计算错误 就比 fix: 改了 OrderService 的一个方法 有用得多。

七、紧急修复

没有单独的 hotfix 分支,紧急问题走的还是同一条路,只是加急:从 mainfix/ 分支,改完开 PR,找人立刻 review 合并。

之所以不为了快而绕过 PR,是因为紧急情况下人最容易出错,这时候恰恰更需要第二双眼睛。多花的五分钟远比事后回滚便宜。


结论是流程要匹配团队规模。Git Flow 本身没问题,但它解决的是我们没有的问题。三五个人、持续部署的场景,主干 + 短生命周期分支 + 强制 PR 这三条就够用了,剩下的规则加得越多,绕过它的人越多。