Appearance
多人协作分支最佳实践:保持干净线性历史
背景问题
在团队开发中,当多人共用一个分支(如 develop、main、release 等)时,如果使用默认的 git pull 或 git merge,会产生大量无意义的 merge commit,导致 Git 历史变得混乱不堪。
默认 Merge 的问题
bash
# 场景:团队成员 A 和 B 同时在 develop 分支开发
# A 提交了 commit A1
# B 提交了 commit B1
# 当 B 先推送后,A 需要拉取最新代码
git pull # 默认执行 merge
# 结果:产生了 merge commit
* merge commit (杂乱无章的 merge 记录)
|\
| * B1 (B 的提交)
* | A1 (A 的提交)
|/
* 之前的提交长期积累后,Git 历史会变成一团乱麻,难以追溯和 code review。
最专业的解决方案:Rebase
核心思想
Rebase(变基) 将你的提交"重放"到目标分支的最新提交之上,保持线性历史。
bash
# 使用 rebase 代替 merge
git pull --rebase
# 效果:没有 merge commit,历史保持线性
* A1 (A 的提交)
* B1 (B 的提交)
* (之前的提交)配置全局设置
一步配置,终身受益:
bash
# 方法一:配置 pull.rebase 为 true
git config --global pull.rebase true
# 验证配置
git config --global --get pull.rebase
# 输出: true或在 ~/.gitconfig 中添加:
ini
[pull]
rebase = true具体操作流程
日常开发流程
bash
# 1. 确保本地分支是最新的(无未提交的修改)
git status
# 2. 切换到目标协作分支
git checkout develop
# 3. 拉取最新代码(自动 rebase,不再产生 merge commit)
git pull
# 4. 从最新代码创建自己的功能分支
git checkout -b feature/your-feature
# 5. 开发完成后,合并回协作分支
git checkout develop
git merge feature/your-feature
# 6. 推送到远程
git push遇到冲突时的处理
bash
# Rebase 过程中遇到冲突
git rebase develop
# Git 会暂停在冲突的提交处,修复冲突后:
git add .
# 继续 rebase
git rebase --continue
# 或者放弃 rebase,恢复到原始状态
git rebase --abort使用 Git Stash 避免 Merge(推荐)
git stash 是处理未提交代码的利器,尤其适合在拉取最新代码前临时保存本地修改,避免产生 merge commit。
场景一:本地有修改,需要拉取最新代码
有两种方案,根据你的实际情况选择:
方案 A:改一半,不想提交
bash
# 场景:代码改了一半,还没想好怎么提交
git stash
git fetch
git rebase
git stash pop方案 B:愿意临时 commit
bash
# 场景:已完成某个阶段性修改
git add .
git commit -m "tmp"
git fetch
git rebase
# 继续开发,后面可以 git commit --amend 合并提交两种方案对比
| 方案 A (git stash) | 方案 B (临时 commit) | |
|---|---|---|
| 适用 | 改一半、没想好怎么提交 | 已完成某个阶段性修改 |
| 优点 | 不污染提交历史 | 随时可中断,有备份 |
| 缺点 | stash 多了难管理 | 多了个 "tmp" commit |
更简洁的写法:如果已配置
git config --global pull.rebase true,可以直接用git pull --rebase代替git fetch+git rebase
场景二:切换分支前暂存修改
bash
# 场景:正在 develop 分支开发,需要临时切换到其他分支处理紧急任务
# 1. 暂存当前修改
git stash
# 2. 切换到其他分支处理紧急任务
git checkout hotfix/urgent-bug
# ... 修复 bug ...
git checkout develop
# 3. 恢复开发中的修改
git stash pop场景三:使用 Git Stash + Rebase 完整流程
bash
# 完整的多人协作流程示例
# 1. 开始工作前,确保本地分支是最新的
git checkout develop
git pull --rebase
# 2. 创建/切换到功能分支
git checkout -b feature/my-feature
# 3. 在功能分支上开发(多次 commit)
git commit -m "feat: 添加功能模块A"
git commit -m "feat: 添加功能模块B"
# 4. 开发完成,需要同步 develop 的最新改动
git stash # 暂存功能分支的 commit(stash 会包含未 push 的 commit)
git checkout develop
git pull --rebase # 拉取最新,无 merge commit
git checkout feature/my-feature
git stash pop # 恢复并 rebase
# 5. 如果有冲突,按正常流程解决
# git add .
# git rebase --continue
# 6. 合并回 develop(快进合并,无额外 merge commit)
git checkout develop
git merge feature/my-feature
git push
# 7. 删除功能分支
git branch -d feature/my-feature场景四:使用 Git Worktree 彻底隔离(高级)
如果经常需要在多个分支间切换,可以使用 Git Worktree 避免 stash:
bash
# 创建新的 worktree(每个分支在独立目录)
git worktree add ../feature-login feature/login
# 在新目录中工作,互不干扰
cd ../feature-login
# ... 开发 ...
# 回到主目录
cd ..
git checkout develop
git pull --rebaseGit Stash 进阶技巧
常用命令速查
bash
git stash # 暂存当前修改
git stash save "message" # 暂存并添加描述
git stash list # 查看所有 stash
git stash pop # 恢复最新 stash 并删除
git stash apply # 恢复最新 stash 但保留记录
git stash apply stash@{n} # 恢复指定 stash
git stash drop # 删除最新 stash
git stash clear # 清空所有 stash
git stash show # 查看 stash 内容
git stash show -p # 查看 stash 详细改动多人协作 Stash 规范建议
bash
# 在 ~/.gitconfig 中添加 stash 配置
[stash]
commit = true # 自动为 stash 创建 commit
includeUntracked = true # 包含未跟踪的文件
# 设置 stash 的引用日志保留时间
[core]
reflogExpireReflogUnrefreachable = 90 days常见问题
| 问题 | 解决方案 |
|---|---|
| stash 太多找不到 | git stash list + git stash show stash@{n} |
| pop 时冲突 | git stash show -p stash@{n} > fix.patch 保存后手动处理 |
不小心 git stash drop | git fsck --unreachable 找回 dangling commit |
| stash 占用空间 | git stash prune 清理无用的 stash 对象 |
推荐的工作流
Feature Branch 工作流
main (保护分支)
|
└── develop (协作分支,使用 rebase)
|
└── feature/xxx (功能分支,开发完成后合并)bash
# 1. 保持 develop 分支始终使用 rebase
git checkout develop
git pull --rebase
# 2. 从 develop 创建功能分支
git checkout -b feature/login
# 3. 在功能分支上开发
git commit -m "feat: 添加登录功能"
# 4. 功能完成后,先 rebase 到最新的 develop
git checkout develop
git pull --rebase # 确保 develop 是最新的
git checkout feature/login
git rebase develop # 将功能分支 rebase 到 develop
# 5. 切换到 develop,合并功能分支(快进合并,不产生额外 commit)
git checkout develop
git merge feature/login
# 6. 删除功能分支,推送
git branch -d feature/login
git push紧急修复流程
bash
# 紧急情况下,直接在 develop 分支修复
git checkout develop
git pull --rebase # 拉取最新,保持干净
# 紧急修复
git commit -m "fix: 修复紧急 bug"
# 推送
git push团队规范建议
1. 强制保护分支
在 GitLab/GitHub 设置中:
- 保护
main、develop等核心分支 - 禁止直接推送,需要通过 MR/PR 合并
- 至少需要 1-2 人 review 通过
2. 设置 Git 全局配置
在项目根目录创建 .gitconfig.local,团队成员各自运行:
bash
git config --local include.path ../.gitconfig.local或在全局推广:
bash
# 在新员工入职文档中添加
git config --global pull.rebase true
git config --global rebase.autostash true # 自动暂存未提交的修改3. 配置 Git alias 简化操作
bash
# 在 ~/.gitconfig 中添加别名
[alias]
pl = pull --rebase
st = status
co = checkout
cb = checkout -b使用:
bash
git pl # 等价于 git pull --rebase常见问题
Q: 什么时候应该用 merge 而不是 rebase?
| 场景 | 推荐操作 |
|---|---|
| 多人协作的长期分支(如 develop) | ✅ Rebase |
| 合并 feature 分支到主分支 | ✅ Merge(快进)或 Rebase |
| 保留分支完整历史 | ✅ Merge |
| 紧急 hotfix | ✅ Merge |
| Pull Request / Merge Request | ✅ Squash + Rebase |
Q: 已经产生了大量 merge commit 怎么办?
bash
# 可以使用 git rebase -i 清理历史
git rebase -i HEAD~10
# 在交互式界面中,将需要保留的提交改为 squashQ: rebase 失败了怎么恢复?
bash
# rebase 过程中可以随时中止
git rebase --abort
# 如果已经完成,想回到 rebase 之前
git reflog
# 找到 rebase 前的 commit hash
git reset --hard HEAD@{N}总结
| 方法 | 优点 | 缺点 |
|---|---|---|
| 默认 merge | 操作简单,保留完整历史 | 产生大量 merge commit,历史混乱 |
| Rebase | 历史线性清晰,便于追溯 | 需要团队统一规范 |
| Squash Merge | 历史极度干净 | 丢失单独的 commit 信息 |
最佳实践:对于多人协作的分支,始终使用 git pull --rebase,并配置 git config --global pull.rebase true,这是最专业、最干净、最不会产生垃圾 merge 记录的做法。