Git 工作流模式
Git 版本控制、分支策略和协作开发的最佳实践。
何时激活
- 为新项目设置 Git 工作流
- 决定分支策略(GitFlow、基于主干开发、GitHub Flow)
- 编写提交信息和 PR 描述
- 解决合并冲突
- 管理发布和版本标签
- 让新团队成员熟悉 Git 实践
分支策略
GitHub Flow(简单,推荐大多数团队使用)
最适合持续部署和小型到中型团队。
main (受保护,始终可部署)
│
├── feature/user-auth → PR → 合并到 main
├── feature/payment-flow → PR → 合并到 main
└── fix/login-bug → PR → 合并到 main
规则:
main始终是可部署状态- 从
main创建特性分支 - 准备好审查时创建 Pull Request
- 审批通过且 CI 通过后,合并到
main - 合并后立即部署
基于主干开发(高速团队)
最适合具备强大 CI/CD 和功能开关的团队。
main (主干)
│
├── 短期特性分支 (最多1-2天)
├── 短期特性分支
└── 短期特性分支
规则:
- 所有人都提交到
main或极短期的分支 - 功能开关隐藏未完成的工作
- 合并前必须通过 CI
- 每天部署多次
GitFlow(复杂,发布周期驱动)
最适合计划发布的团队和企业级项目。
main (生产发布)
│
└── develop (集成分支)
│
├── feature/user-auth
├── feature/payment
│
├── release/1.0.0 → 合并到 main 和 develop
│
└── hotfix/critical → 合并到 main 和 develop
规则:
main只包含生产就绪代码develop是集成分支- 特性分支从
develop创建,合并回develop - 发布分支从
develop创建,合并到main和develop - 热修复分支从
main创建,合并到main和develop
何时使用哪种策略
| 策略 | 团队规模 | 发布频率 | 最佳适用场景 |
|---|---|---|---|
| GitHub Flow | 任意 | 持续 | SaaS、Web 应用、初创公司 |
| 基于主干开发 | 5人以上有经验 | 每天多次 | 高速团队、功能开关 |
| GitFlow | 10人以上 | 计划发布 | 企业、受监管行业 |
提交信息
约定式提交格式
<类型>(<作用域>): <主题>
[可选正文]
[可选脚注]
类型
| 类型 | 用途 | 示例 |
|---|---|---|
feat | 新功能 | feat(auth): add OAuth2 login |
fix | 错误修复 | fix(api): handle null response in user endpoint |
docs | 文档 | docs(readme): update installation instructions |
style | 格式调整,无代码改动 | style: fix indentation in login component |
refactor | 代码重构 | refactor(db): extract connection pool to module |
test | 添加/更新测试 | test(auth): add unit tests for token validation |
chore | 维护任务 | chore(deps): update dependencies |
perf | 性能改进 | perf(query): add index to users table |
ci | CI/CD 变更 | ci: add PostgreSQL service to test workflow |
revert | 还原之前的提交 | revert: revert "feat(auth): add OAuth2 login" |
好与坏的示例
# 错误:模糊,无上下文
git commit -m "fixed stuff"
git commit -m "updates"
git commit -m "WIP"
# 正确:清晰、具体,解释原因
git commit -m "fix(api): retry requests on 503 Service Unavailable
外部 API 在高峰时段偶尔返回 503 错误。
添加了指数退避重试逻辑,最多尝试 3 次。
Closes #123"
提交信息模板
在仓库根目录创建 .gitmessage:
# <类型>(<作用域>): <主题>
# # 类型: feat, fix, docs, style, refactor, test, chore, perf, ci, revert
# 作用域: api, ui, db, auth 等
# 主题: 祈使语气,无句号,最多 50 个字符
#
# [可选正文] - 解释为什么,而不是什么
# [可选脚注] - 破坏性变更,关闭 #issue
启用命令:git config commit.template .gitmessage
合并 vs 变基
合并(保留历史)
# 创建一个合并提交
git checkout main
git merge feature/user-auth
# 结果:
# * 合并提交
# |\
# | * 特性提交
# |/
# * main 提交
何时使用:
- 将特性分支合并到
main时 - 希望保留精确历史时
- 多人协作该分支时
- 分支已被推送,且其他人可能基于它工作时
变基(线性历史)
# 将特性提交重写到目标分支之上
git checkout feature/user-auth
git rebase main
# 结果:
# * 特性提交(已重写)
# * main 提交
何时使用:
- 用最新的
main更新本地特性分支时 - 希望得到线性、清晰的历史时
- 分支仅在本地上(尚未推送)时
- 你独自在该分支上工作时
变基工作流
# 在 PR 之前,用最新的 main 更新特性分支
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 修复任何冲突
# 测试仍应通过
# 强制推送(仅在你独自贡献时)
git push --force-with-lease origin feature/user-auth
何时不应变基
# 绝不应变基以下分支:
- 已推送到共享仓库的分支
- 他人基于其工作的分支
- 受保护的分支 (main, develop)
- 已合并的分支
# 原因:变基重写历史,会破坏他人的工作
Pull Request 工作流
PR 标题格式
<类型>(<作用域>): <描述>
示例:
feat(auth): add SSO support for enterprise users
fix(api): resolve race condition in order processing
docs(api): add OpenAPI specification for v2 endpoints
PR 描述模板
## 做了什么
本 PR 所做内容的简要描述。
## 为什么
解释动机和背景。
## 如何实现
值得强调的关键实现细节。
## 测试
- [ ] 已添加/更新单元测试
- [ ] 已添加/更新集成测试
- [ ] 已执行手动测试
## 截图(如适用)
UI 变更的前后截图。
## 检查清单
- [ ] 代码遵循项目样式指南
- [ ] 已完成自我审查
- [ ] 为复杂逻辑添加了注释
- [ ] 文档已更新
- [ ] 未引入新的警告
- [ ] 测试在本地通过
- [ ] 关联了相关 issue
Closes #123
代码审查检查清单
对于审查者:
- 代码是否解决了所述问题?
- 是否有未处理的边界情况?
- 代码是否可读且可维护?
- 是否有足够的测试?
- 是否存在安全问题?
- 提交历史是否清晰(如需要已压缩)?
对于作者:
- 在请求审查前已完成自我审查
- CI 通过(测试、lint、类型检查)
- PR 大小合理(理想情况下 <500 行)
- 与单个功能/修复相关
- 描述清晰解释了变更内容
冲突解决
识别冲突
# 在合并前检查冲突
git checkout main
git merge feature/user-auth --no-commit --no-ff
# 如果有冲突,Git 会显示:
# CONFLICT (content): Merge conflict in src/auth/login.ts
# Automatic merge failed; fix conflicts and then commit the result.
解决冲突
# 查看冲突文件
git status
# 查看文件中的冲突标记
# <<<<<<< HEAD
# content from main
# =======
# content from feature branch
# >>>>>>> feature/user-auth
# 选项 1:手动解决
# 编辑文件,移除标记,保留正确内容
# 选项 2:使用合并工具
git mergetool
# 选项 3:接受一侧
git checkout --ours src/auth/login.ts # 保留 main 版本
git checkout --theirs src/auth/login.ts # 保留特性版本
# 解决后,暂存并提交
git add src/auth/login.ts
git commit
冲突预防策略
# 1. 保持特性分支小巧且短期
# 2. 经常变基到 main
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 3. 与团队沟通涉及共享文件的变更
# 4. 使用功能开关代替长期分支
# 5. 及时审查并合并 PR
分支管理
命名规范
# 特性分支
feature/user-authentication
feature/JIRA-123-payment-integration
# 错误修复
fix/login-redirect-loop
fix/456-null-pointer-exception
# 热修复(生产问题)
hotfix/critical-security-patch
hotfix/database-connection-leak
# 发布
release/1.2.0
release/2024-01-hotfix
# 实验/POC
experiment/new-caching-strategy
poc/graphql-migration
分支清理
# 删除已合并的本地分支
git branch --merged main | grep -v "^\*\|main" | xargs -n 1 git branch -d
# 删除已删除远程分支的远程跟踪引用
git fetch -p
# 删除本地分支
git branch -d feature/user-auth # 安全删除(仅当已合并时)
git branch -D feature/user-auth # 强制删除
# 删除远程分支
git push origin --delete feature/user-auth
储藏工作流
# 保存进行中的工作
git stash push -m "WIP: user authentication"
# 列出储藏
git stash list
# 应用最近的储藏
git stash pop
# 应用指定的储藏
git stash apply stash@{2}
# 删除储藏
git stash drop stash@{0}
发布管理
语义化版本
MAJOR.MINOR.PATCH
MAJOR: 破坏性变更
MINOR: 新功能,向后兼容
PATCH: 错误修复,向后兼容
示例:
1.0.0 → 1.0.1 (补丁:错误修复)
1.0.1 → 1.1.0 (次要:新功能)
1.1.0 → 2.0.0 (主要:破坏性变更)
创建发布
# 创建带注释的标签
git tag -a v1.2.0 -m "Release v1.2.0
功能:
- 添加用户认证
- 实现密码重置
修复:
- 解决登录重定向问题
破坏性变更:
- 无"
# 将标签推送到远程
git push origin v1.2.0
# 列出标签
git tag -l
# 删除标签
git tag -d v1.2.0
git push origin --delete v1.2.0
生成变更日志
# 从提交生成变更日志
git log v1.1.0..v1.2.0 --oneline --no-merges
# 或使用 conventional-changelog
npx conventional-changelog -i CHANGELOG.md -s
Git 配置
基本配置
# 用户身份
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
# 默认分支名
git config --global init.defaultBranch main
# 拉取行为(使用变基而非合并)
git config --global pull.rebase true
# 推送行为(仅推送当前分支)
git config --global push.default current
# 自动纠正拼写错误
git config --global help.autocorrect 1
# 更好的差异算法
git config --global diff.algorithm histogram
# 彩色输出
git config --global color.ui auto
有用的别名
# 添加到 ~/.gitconfig
[alias]
co = checkout
br = branch
ci = commit
st = status
unstage = reset HEAD --
last = log -1 HEAD
visual = log --oneline --graph --all
amend = commit --amend --no-edit
wip = commit -m "WIP"
undo = reset --soft HEAD~1
contributors = shortlog -sn
Gitignore 模式
# 依赖
node_modules/
vendor/
# 构建输出
dist/
build/
*.o
*.exe
# 环境文件
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# 操作系统文件
.DS_Store
Thumbs.db
# 日志
*.log
logs/
# 测试覆盖率
coverage/
# 缓存
.cache/
*.tsbuildinfo
常见工作流
开始一个新功能
# 1. 更新 main 分支
git checkout main
git pull origin main
# 2. 创建特性分支
git checkout -b feature/user-auth
# 3. 进行更改并提交
git add .
git commit -m "feat(auth): implement OAuth2 login"
# 4. 推送到远程
git push -u origin feature/user-auth
# 5. 在 GitHub/GitLab 上创建 Pull Request
用新更改更新 PR
# 1. 进行额外的更改
git add .
git commit -m "feat(auth): add error handling"
# 2. 推送更新
git push origin feature/user-auth
同步 Fork 与上游仓库
# 1. 添加上游远程仓库(仅一次)
git remote add upstream https://github.com/original/repo.git
# 2. 获取上游仓库
git fetch upstream
# 3. 将上游 main 合并到你的 main
git checkout main
git merge upstream/main
# 4. 推送到你的 fork
git push origin main
撤销错误
# 撤销上次提交(保留更改)
git reset --soft HEAD~1
# 撤销上次提交(丢弃更改)
git reset --hard HEAD~1
# 撤销已推送到远程的上次提交
git revert HEAD
git push origin main
# 撤销特定文件的更改
git checkout HEAD -- path/to/file
# 修改上次提交的信息
git commit --amend -m "New message"
# 将遗漏的文件添加到上次提交
git add forgotten-file
git commit --amend --no-edit
Git Hooks
Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
# 运行 lint
git diff --cached --name-only | xargs npm run lint || exit 1
# 运行测试
npm test || exit 1
# 检查是否存在机密信息
if git diff --cached | grep -E '(password|api_key|secret)'; then
echo "检测到可能的机密信息。提交已中止。"
exit 1
fi
Pre-Push Hook
#!/bin/bash
# .git/hooks/pre-push
# 运行完整测试套件
npm run test:all || exit 1
# 检查是否有 console.log 语句
if git diff origin/main | grep -E 'console\.log'; then
echo "在推送前移除 console.log 语句。"
exit 1
fi
反模式
# 错误:直接提交到 main
git checkout main
git commit -m "fix bug"
# 正确:使用特性分支和 PR
# 错误:提交机密信息
git add .env # 包含 API 密钥
# 正确:添加到 .gitignore,使用环境变量
# 错误:巨大的 PR(1000+ 行)
# 正确:拆分为更小、更聚焦的 PR
# 错误:"更新" 提交信息
git commit -m "update"
git commit -m "fix"
# 正确:描述性信息
git commit -m "fix(auth): resolve redirect loop after login"
# 错误:重写公共历史
git push --force origin main
# 正确:对公共分支使用 revert
git revert HEAD
# 错误:长期存在的特性分支(数周/数月)
# 正确:保持分支短期(数天),经常变基
# 错误:在共享分支上使用 `git push --force`
# 正确:使用 `git push --force-with-lease` 作为更安全的选择
# 错误:混合风格(在同一项目中混用缩进和 lint 规则)
# 正确:使用 .editorconfig 和 prettier 强制执行一致的风格