📌 写在前面

以前自己一个人写项目的时候,Git对我来说就是个“高级另存为”:一个master分支从头走到尾,写完就git push,从没觉得分支管理有什么难的。

直到最近和朋友一起开发一个项目,问题来了:他改了一版,我也改了一版,两个人同时改同一个文件,推上去就冲突;他想要加个新功能,我想修个紧急Bug,分支不知道该怎么建;代码写完了,谁来审核?怎么合并?一不小心就把对方的工作覆盖了……

这时候才意识到:团队协作,不是把代码堆在一起就行,而是需要一套约定好的“交通规则”。GitFlow、GitHub Flow、rebase、merge、Pull Request……这些名词突然从“面试八股”变成了“救命指南”。

这篇笔记,我以一个“刚从单干转成团队协作”的开发者视角,梳理Git分支管理的主流策略、操作实践和Commit规范。希望你看完后,能和队友建立起一套清晰、高效、不吵架的协作流程。

1️⃣ 为什么团队需要分支管理规范?

单兵作战时,只有一个master分支,所有改动都直接提交,没有任何问题。但当两个人甚至更多人同时开发时,就会出现:

  • 代码互相覆盖:两个人改了同一个文件,后推送的人被迫解决冲突,甚至丢失对方的修改。

  • 功能交错污染:新功能还没写完,紧急Bug修复却必须等它完成才能发布。

  • 历史一团乱麻:提交记录乱七八糟,回滚某个功能时不知道从哪下手。

  • 无法Code Review:没人审核代码,低质量代码直接进入主分支。

分支管理规范的核心目的就是:让不同人的工作隔离,让不同性质的工作隔离,让每一次合并都有记录和审核

2️⃣ 主流分支模型对比

GitFlow —— 最经典、最正式的分支模型

GitFlow由Vincent Driessen于2010年提出,适用于有明确版本发布周期的项目。

核心分支

  • master:存放正式发布版本,每个提交对应一个上线版本(通常打tag)。

  • develop:集成分支,所有功能开发最终合并到这里。

  • feature/*:功能分支,从develop拉出,完成后合并回develop

  • release/*:发布分支,从develop拉出,用于发布前的最终测试和Bug修复,完成后合并回masterdevelop

  • hotfix/*:紧急修复分支,从master拉出,修复线上Bug,完成后合并回masterdevelop

优点:结构清晰,适合需要多版本并行维护的项目(如传统软件、游戏)。
缺点:分支较多,对于持续交付的Web项目来说过于繁琐。

GitHub Flow —— 极简、适合持续交付

GitHub推崇的轻量级模型,只有一条长期分支main(原master),以及临时的功能分支。

核心规则

  • main分支任何时候都是可部署的。

  • 新功能/修复都从main拉出新分支,命名如feature/xxxfix/xxx

  • 完成后创建Pull Request,经过审核后合并回main

  • 合并后立即(或自动)部署。

优点:简单、适合快速迭代的Web应用和SaaS。
缺点:没有develop分支,对大型团队或需要严格发布周期的项目支持不足。

GitLab Flow —— 环境分支的补充

GitLab Flow在GitHub Flow基础上增加了“环境分支”,如pre-productionproduction,用于控制不同环境的部署。

选择建议

  • 小型团队、Web项目、快速迭代 → GitHub Flow

  • 中型团队、有明确发版周期、需要同时维护多个版本 → GitFlow

  • 需要多环境部署(如预发、生产分离)→ GitLab Flow

我们和朋友一起开发的个人项目,通常2-5人,推荐GitHub Flow,简单够用。如果你想要更严谨的流程,可以用简化版GitFlow(保留developfeature,但不需要releasehotfix分支)。

3️⃣ 分支命名与保护规则

分支命名规范

统一的分支命名能让团队成员一眼看出分支的用途:

分支保护规则

在GitHub/GitLab等平台上,可以对maindevelop分支设置保护:

  1. 禁止直接推送:所有人不能直接push到受保护分支,只能通过Pull Request/Merge Request合并。

  2. 要求PR审核:合并前至少需要1-2人批准。

  3. 要求状态检查通过:如CI测试必须通过才能合并。

  4. 线性历史要求:可选,强制使用rebase而非merge。

对于和朋友的小项目,至少设置禁止直接push要求1人审核,能避免很多低级错误。

4️⃣ 合并策略:merge vs rebase,以及冲突解决

这是最容易让新手困惑的地方。先理解两者的本质区别:

  • merge:将两个分支的历史合并,生成一个新的合并提交,保留完整的分支历史。

  • rebase:将当前分支的提交“变基”到目标分支的最新提交之上,重写提交历史,形成线性历史。

什么时候用merge?

适用场景:合并长期存在的功能分支到主分支,或需要保留分支的并行开发脉络。

# 在 feature 分支上
git checkout feature
git merge main   # 把 main 的最新代码合并到 feature(解决冲突)
# 然后回到 main,再合并 feature
git checkout main
git merge feature --no-ff   # 保留分支信息

什么时候用rebase?

适用场景:在个人分支上保持与主分支同步,避免产生无意义的合并提交。

# 在 feature 分支上
git checkout feature
git rebase main   # 把 feature 的提交“重放”到 main 的最新提交上
# 如果有冲突,解决后 git add . && git rebase --continue
# 然后回到 main 合并(此时是快进合并)
git checkout main
git merge feature   # 线性历史

冲突解决实战

git mergegit rebase提示冲突时,Git会在文件中标记冲突区域:

<<<<<<< HEAD
当前分支的代码
=======
目标分支的代码
>>>>>>> branch-name

解决步骤

  1. 手动编辑文件,保留需要的代码,删除冲突标记。

  2. git add <文件名>

  3. 如果是merge,git commit(自动生成合并信息);如果是rebase,git rebase --continue

团队建议

  • 公共分支(如main)严禁使用rebase,因为会改写历史,导致其他人的本地仓库混乱。

  • 个人分支可以随意使用rebase,保持提交整洁。

  • 合并到主分支时,使用git merge --no-ff保留分支记录,方便追溯。

5️⃣ Commit规范:让提交历史可读可追溯

团队协作中,提交信息不是写给自己看的,而是写给队友和未来的自己看的。遵循Conventional Commits规范,可以让提交历史一目了然。

格式

<type>(<scope>): <subject>

[optional body]

[optional footer]

常用type

示例

feat(auth): 添加JWT刷新令牌接口

- 新增refreshToken端点
- 增加Token过期自动续期逻辑

Closes #123

fix(cart): 修复商品数量为0时仍可结算的Bug

团队实践建议

  1. 每次提交只做一件事:不要一个提交包含多个不相关的修改。

  2. 写清楚“为什么”:不是“修改了a.js”,而是“修复了列表加载时内存泄漏问题”。

  3. 强制规范工具:可以用commitizenhusky + commitlint在提交时自动校验格式。

6️⃣ 完整的团队协作流程(基于GitHub Flow)

下面是一个典型的、适合2-5人小团队的协作流程:

关键点详解

1. 每次开发前同步主分支

git checkout main
git pull origin main
git checkout -b feature/xxx   # 从最新的main切出新分支

2. 提交到远程自己的分支

git push origin feature/xxx

3. 发起Pull Request(PR)

  • 在GitHub/GitLab上创建PR,填写清晰描述。

  • 关联相关Issue(如有)。

  • 指定审核人(至少1人,小团队可以互相审核)。

4. 代码审核

  • 审核人查看代码变动,留言提问或建议修改。

  • 开发者根据反馈修改,再次push,PR会自动更新。

  • 审核通过后,点击合并。

5. 合并后处理

  • 删除已合并的远程分支(仓库通常自动提供选项)。

  • 其他开发者git pull origin main同步最新代码。

谁负责审核?

你问“提交后谁来审核”——在小团队里,通常就是互相审核。没有固定的审核人,谁有空谁审,但需要约定:

  • 每个人提交的PR必须至少被一个人审核通过才能合并。

  • 审核人不是“走过场”,要真正检查代码逻辑、潜在Bug、是否符合规范。

  • 如果团队成员较少(比如2个人),可以约定:自己不能合并自己的PR,必须由对方审核。

大型团队会有Code Owner机制,但小项目从“互相审核”开始就足够了。

7️⃣ 常见场景与避坑指南

场景一:不小心在main分支上直接提交了代码

解决方案

# 在main分支上创建一个新分支来保存这次提交
git checkout -b feature/accidental-commit
# 将main分支回退到上一个提交
git reset --hard HEAD~1
# 推送修改(可能需要强制推送,但main分支受保护时不允许)
git push origin main --force-with-lease  # 慎用

最好开启分支保护,从源头禁止直接push。

场景二:push时提示“远程有新的提交,需要先pull”

git pull --rebase origin main   # 使用rebase避免产生多余的合并提交
# 解决冲突后
git push origin feature/xxx

场景三:合并PR后发现出现了Bug,需要回滚

# 找到合并前的提交ID
git log --oneline
# 回滚到那个提交(会产生一个反向提交)
git revert -m 1 <merge-commit-id>
git push origin main

避坑指南

  1. 不要对公共分支使用rebase:会改写历史,导致队友的本地仓库与远程不一致。

  2. 不要在pull时使用普通的git pull(等同于merge)产生多余的合并提交:建议配置git config --global pull.rebase true

  3. 禁止强制推送(--force)到公共分支:除非你非常清楚后果,推荐使用--force-with-lease

  4. PR不要太大:一个PR最好只做一件事,超过400行变动的PR会很难审。

  5. 合并前确保CI通过:如果有自动化测试,绿色才能合并。

8️⃣ 总结与工具推荐

推荐工具

  • Git GUI:SourceTree、Fork(Mac)、GitKraken

  • GitHooks:husky + commitlint 强制Commit规范

  • PR模板:在仓库根目录创建.github/pull_request_template.md,引导填写描述。

你和朋友/同事一起开发时,是否遇到过“合并冲突地狱”?比如两个人同时改了同一个文件的同一块代码,解决冲突时甚至改乱了逻辑。请分享一次你最难忘的冲突解决经历,或者你认为能有效减少冲突的团队约定(比如模块划分、分支周期等)。

(欢迎在评论区交流你的协作经验)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐