Git 分支模型
分支是 Git 最强大的特性之一。合理的分支策略能让团队协作更加高效。以下是两种主流的分支工作流。
Git Flow
适合有固定发版周期的项目:
main ─── 生产环境代码,每次发布打 tag
└── develop ─── 开发主线,集成所有功能
├── feature/xxx ─── 功能分支,从 develop 创建
├── release/x.x ─── 发布准备分支
└── hotfix/xxx ─── 紧急修复,从 main 创建
# Git Flow 典型操作
git checkout develop
git checkout -b feature/user-auth
# 开发完成后合并回 develop
git checkout develop
git merge --no-ff feature/user-auth
git branch -d feature/user-auth
# 发布新版本
git checkout -b release/1.2.0 develop
# 修复小 bug、更新版本号...
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
git checkout develop
git merge --no-ff release/1.2.0
Trunk-Based Development
适合持续部署的项目(Google、Facebook 均采用):
main ──────────────────────────────────── 持续集成
├── feature/small-change-1 (短命分支,1-2天)
├── feature/small-change-2
└── feature/small-change-3
核心原则:频繁集成、短命分支、功能开关(Feature Flags)控制未完成功能的可见性。
Rebase —— 重写提交历史
Rebase 是 Git 中最强大也最容易误用的功能。它能将一系列提交"移植"到新的基点上。
基本 Rebase
# 场景:feature 分支落后于 main
# merge 方式:产生一个合并提交
git checkout feature
git merge main
# 结果: A-B-C-D-E-F-G-M (M 是合并提交)
# rebase 方式:将 feature 的提交"移到" main 最新提交之后
git checkout feature
git rebase main
# 结果: A-B-C-D-E-F-G-H-I (线性历史,更清晰)
交互式 Rebase —— 整理提交历史
# 整理最近 5 个提交
git rebase -i HEAD~5
# 编辑器会打开:
pick a1b2c3d 添加用户模型
pick e4f5g6h 修复 typo
pick i7j8k9l 添加用户路由
pick m0n1o2p WIP: 还没写完
pick q3r4s5t 添加测试
# 常用操作:
# pick = 保留提交
# reword = 修改提交信息
# squash = 合并到上一个提交,保留信息
# fixup = 合并到上一个提交,丢弃信息
# drop = 删除提交
# edit = 暂停,允许修改内容
# 修改为:
pick a1b2c3d 添加用户模型
fixup e4f5g6h 修复 typo # 合并到上一个
pick i7j8k9l 添加用户路由
drop m0n1o2p WIP: 还没写完 # 删除
pick q3r4s5t 添加测试
Rebase 的黄金规则
永远不要 rebase 已经推送到远程的、其他人正在使用的分支。
Rebase 会改写提交历史(改变 commit hash),如果其他人已经基于旧历史开发,会导致冲突和混乱。只在本地分支或个人分支上使用 rebase。
Cherry-Pick —— 精确移植提交
# 将某个提交应用到当前分支
git cherry-pick abc1234
# 一次性 cherry-pick 多个提交
git cherry-pick abc1234 def5678 ghi9012
# cherry-pick 一个范围(不含起始提交)
git cherry-pick abc1234..ghi9012
# cherry-pick 但不自动提交(可以继续修改)
git cherry-pick --no-commit abc1234
# 实际场景:修复 bug 在 develop 上,需要同步到 release 分支
git checkout release/1.2.0
git cherry-pick <bug-fix-commit-hash>
Bisect —— 用二分法定位 Bug
当发现某个 bug 但不知道是哪个提交引入时,git bisect 能帮你用二分法快速定位。
# 开始 bisect
git bisect start
# 标记当前版本有 bug
git bisect bad
# 标记某个旧版本没有 bug
git bisect good v1.0.0
# Git 会自动 checkout 到中间的提交,你测试后告诉它结果:
# 测试这个版本
git bisect bad # 有 bug
# 或
git bisect good # 没有 bug
# Git 继续二分,直到找到引入 bug 的那个提交
# 最终输出:abc1234 is the first bad commit
# 结束 bisect,回到原来的分支
git bisect reset
# 自动化 bisect(用脚本测试)
git bisect start HEAD v1.0.0
git bisect run npm test
# Git 会自动运行 npm test,根据返回码判断 good/bad
Stash —— 临时保存工作区
# 保存当前工作区(包括已暂存的修改)
git stash push -m "正在开发的功能"
# 查看所有 stash
git stash list
# stash@{0}: On feature: 正在开发的功能
# stash@{1}: On feature: 另一个修改
# 恢复最近的 stash(保留 stash 记录)
git stash apply
# 恢复并删除 stash 记录
git stash pop
# 恢复指定的 stash
git stash apply stash@{1}
# 只 stash 指定文件
git stash push -m "只保存这个文件" -- src/app.js
# 包含未追踪的文件
git stash push -u -m "包含新文件"
# 从 stash 创建新分支
git stash branch new-feature-branch stash@{0}
# 删除所有 stash
git stash clear
实用技巧合集
修改最近的提交
# 修改最后一次提交的信息
git commit --amend -m "新的提交信息"
# 追加文件到上一次提交(不改信息)
git add forgotten-file.js
git commit --amend --no-edit
查看精美日志
# 一行式日志,显示分支图
git log --oneline --graph --all --decorate
# 设置别名
git config --global alias.lg "log --oneline --graph --all --decorate --date=short --format='%C(yellow)%h%C(reset) %C(green)%ad%C(reset) %s %C(blue)%an%C(reset) %C(red)%d%C(reset)'"
# 使用
git lg
找回误删的分支或提交
# 查看所有操作记录(包括已删除的分支)
git reflog
# 输出示例:
# abc1234 HEAD@{0}: checkout: moving from main to feature
# def5678 HEAD@{1}: commit: 添加新功能
# ghi9012 HEAD@{2}: branch: Created branch feature
# 恢复被删除的分支
git checkout -b feature-restored def5678
# 恢复被 reset 丢失的提交
git reset --hard HEAD@{2}
对比和调试
# 比较工作区和暂存区
git diff
# 比较暂存区和最后一次提交
git diff --staged
# 比较两个分支
git diff main..feature --stat # 只看文件统计
git diff main..feature -- src/ # 只看 src 目录
# 查看某个文件的每一行是谁在什么时候修改的
git blame src/app.js
# 搜索提交历史中的代码变更
git log -S "functionName" --oneline # 搜索代码内容变化
git log --grep="fix" --oneline # 搜索提交信息
.gitconfig 推荐配置
# ~/.gitconfig
[user]
name = 陈博豪
email = bohao@example.com
[alias]
co = checkout
br = branch
ci = commit
st = status
lg = log --oneline --graph --all --decorate
unstage = reset HEAD --
last = log -1 HEAD --stat
amend = commit --amend --no-edit
[core]
autocrlf = input # Linux/Mac
editor = code --wait # VS Code 作为默认编辑器
[pull]
rebase = true # git pull 时默认使用 rebase
[push]
default = current # push 当前分支到同名远程分支
[diff]
tool = vscode
[difftool]
prompt = false
[merge]
tool = vscode
conflictstyle = diff3 # 显示三方合并冲突
团队协作最佳实践
- 提交信息规范 —— 使用 Conventional Commits 格式:
feat: 新功能、fix: 修复 bug、docs: 文档 - 小步提交 —— 每个提交只做一件事,方便 code review 和 revert
- 同步前先 rebase ——
git pull --rebase保持线性历史 - PR 不要太大 —— 一个 PR 控制在 200-400 行变更以内
- 善用 git bisect —— 定位 bug 比手动快 10 倍
- 不要怕 reflog —— 几乎没有什么操作是不可恢复的
总结
Git 的学习曲线虽然陡峭,但掌握这些高级技巧后,你的开发效率会有质的飞跃。建议在日常开发中逐步实践:先熟悉 rebase -i 和 stash,再学习 bisect 和 cherry-pick。记住,reflog 是你的安全网——大胆尝试,错了总能找回来。