Skip to content

多人协作分支最佳实践:保持干净线性历史

背景问题

在团队开发中,当多人共用一个分支(如 developmainrelease 等)时,如果使用默认的 git pullgit 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 --rebase

Git 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 dropgit 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 设置中:

  • 保护 maindevelop 等核心分支
  • 禁止直接推送,需要通过 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

# 在交互式界面中,将需要保留的提交改为 squash

Q: 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 记录的做法。

最近更新