Git 是开发协作的基石——没有它,多人改同一份代码就是灾难。但 Git 用得好和用得差,团队效率天差地别。提交记录乱成一团、分支互相覆盖、合并冲突没人敢碰——这些都是没规范的后果。本篇分享尧图团队实战验证的 Git 使用规范,从分支策略到提交信息,让版本控制真正服务于协作而非制造混乱。
一、分支策略:Git Flow 简化版
分支策略决定了代码怎么流动。完整的 Git Flow 太重,对建站项目没必要。尧图采用简化版:一个长期分支 main(生产代码)+ 临时功能分支。每个需求从 main 拉分支开发,完成后合并回去。这样 main 永远是可部署状态,互不干扰。
# 分支命名规范
main # 生产分支,永远可部署
develop # 开发集成分支(可选,小团队可省)
feature/article-detail # 功能分支:feature/功能名
fix/login-redirect-bug # 修复分支:fix/问题描述
hotfix/xss-vulnerability # 紧急修复:hotfix/问题描述
release/v2.3.0 # 发布分支:release/版本号
# 标准工作流
# 1. 从 main 拉功能分支
git checkout main
git pull origin main
git checkout -b feature/article-detail
# 2. 开发并提交(小步提交,每个提交聚焦一件事)
git add src/ArticleController.php
git commit -m "feat: 文章详情页增加上一篇下一篇导航"
# 3. 推送到远程
git push -u origin feature/article-detail
# 4. 在 Git 平台发起 Pull Request / Merge Request
# 代码评审通过后合并到 main,删除功能分支
关键原则是"分支生命周期要短"——功能分支别活超过一周,越久合并冲突越多。大需求拆成多个小分支依次合并,别憋一个大分支两周才提 PR。尧图要求 PR 必须至少一人评审通过才能合并,这能挡住大部分低级错误。
二、提交信息规范
提交信息是给未来的自己和同事看的。"修改了点东西"、"更新代码"这种废话提交信息,三个月后排查问题时完全看不出改了啥。尧图采用 Conventional Commits 规范,用固定前缀标明提交类型,一眼看出这次改动属于什么性质。
# 提交信息格式:类型(范围): 描述
# 类型固定为以下几种:
feat: 新功能(feature)
fix: 修复 bug
docs: 文档变更
style: 代码格式调整(不影响逻辑,如缩进、空格)
refactor: 重构(既不是新增功能也不是修 bug)
perf: 性能优化
test: 测试相关
chore: 构建/工具/依赖变更
# 完整提交信息(带正文和脚注)
git commit -m "feat(article): 文章详情页增加相关推荐模块" -m "- 读取同分类最新5篇文章
- 侧边栏展示推荐列表
- 接入 Redis 缓存,TTL 1小时" -m "Closes #128"
# 好的提交信息示例
feat(auth): 登录接口增加图形验证码校验
fix(cart): 修复购物车数量为负数的边界问题
perf(home): 首页商品列表改用分页加载,首屏提速40%
refactor(user): 抽取用户状态判断为独立服务类
# 坏的提交信息(禁止)
update
修改
1
asdf
修复了一些问题
规范化的提交信息还有一个隐藏好处:配合工具能自动生成版本日志(CHANGELOG)。尧图用 standard-version 自动根据提交信息生成版本号和更新日志——feat 对应 minor 版本,fix 对应 patch 版本,BREAKING CHANGE 对应 major 版本。版本发布从此不再靠人肉记录。
三、合并冲突与回滚
冲突是协作的常态——两个人改了同一文件同一区域,合并时 Git 无法自动决定保留哪个,需要人工裁决。处理冲突的关键是理解" ours"和"theirs"的含义,以及如何安全回滚错误的合并。
# 合并功能分支到 main(产生冲突的场景)
git checkout main
git merge feature/article-detail
# 冲突!Git 提示:CONFLICT (content): Merge conflict in src/Article.php
# 查看冲突文件,冲突标记长这样:
<<<<<<< HEAD
// main 分支的代码
public function getTitle() { return $this->title; }
=======
// feature 分支的代码
public function getTitle() {
return $this->seo_title ?: $this->title;
}
>>>>>>> feature/article-detail
# 手动编辑:决定保留哪个或合并两者,删除冲突标记
# 修改后标记已解决
git add src/Article.php
git commit # 完成合并提交
# 回滚:合并出问题,撤销这次合并
git revert -m 1 HEAD # -m 1 保留合并前的 main 分支版本
# revert 会新建一个"反向"提交,安全且保留历史
# 危险操作:reset 回到合并前(会丢弃历史,仅本地用)
git reset --hard HEAD~1 # 永远不要对已推送的提交用 reset --hard
# 紧急回滚生产:发布出 bug,快速回到上个版本
git checkout main
git revert HEAD # 撤销最近一次提交(生成反向提交)
git push origin main # 推送,触发重新部署
处理冲突有个原则:别急着 git checkout --theirs 或 --ours 一刀切——盲目选一边会丢掉另一边的代码。必须读懂两边的改动意图,手动融合。尧图曾因冲突处理不当丢了一段重要的鉴权代码,导致后台短暂可未登录访问。从此规定:冲突解决后必须本地跑一遍测试再合并。另外,reset --hard 对已推送的提交是禁区——别人可能已经基于那个提交工作,强推会让历史混乱。生产回滚一律用 revert,它生成新提交而非改写历史,安全可追溯。