强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

04 - 分支管理:branch、merge、rebase、cherry-pick

第四章:分支管理

分支是 Git 最强大的特性之一,让并行开发和实验变得轻而易举。


4.1 分支的本质

在 Git 中,分支仅仅是一个指向某个提交的可移动指针(lightweight pointer)。

       main
         ↓
    C1 → C2 → C3
              ↑
            feature

默认分支通常叫 main(旧项目可能叫 master)。创建新分支时,Git 只创建一个 41 字节的文件,存储目标提交的 SHA-1 哈希值。

# 查看分支文件内容
$ cat .git/refs/heads/main
abc1234def5678901234567890abcdef12345678

4.2 分支基础操作

4.2.1 创建分支

# 创建新分支(不切换)
$ git branch feature-login

# 创建并切换到新分支
$ git checkout -b feature-login
# 或(Git 2.23+ 推荐)
$ git switch -c feature-login

# 基于特定提交创建分支
$ git branch hotfix-abc1234

# 基于远程分支创建本地分支
$ git branch feature origin/feature
$ git switch -c feature origin/feature

# 创建孤儿分支(无历史记录,用于 gh-pages 等)
$ git switch --orphan gh-pages

4.2.2 查看分支

# 查看本地分支
$ git branch
  develop
* main
  feature-login

# * 表示当前分支

# 查看所有分支(含远程)
$ git branch -a
* main
  remotes/origin/main
  remotes/origin/develop

# 查看远程分支
$ git branch -r

# 查看各分支最后提交
$ git branch -v
* main          abc1234 Latest commit message
  develop       def5678 Dev branch commit
  feature-login ghi9012 Add login form

# 查看已合并到当前分支的分支
$ git branch --merged

# 查看未合并的分支
$ git branch --no-merged

# 查看包含特定提交的分支
$ git branch --contains abc1234

4.2.3 切换分支

# 传统方式
$ git checkout feature-login

# 新方式(Git 2.23+,推荐)
$ git switch feature-login

# 切换到上一个分支
$ git switch -
$ git checkout -

# ⚠️ 切换前必须处理未提交的变更
# 方法 1:提交变更
$ git add -A && git commit -m "WIP"

# 方法 2:暂存变更
$ git stash
$ git switch feature
$ git stash pop

# 方法 3:强制切换(危险!可能丢失变更)
$ git switch -f feature

4.2.4 重命名分支

# 重命名当前分支
$ git branch -m new-name

# 重命名指定分支
$ git branch -m old-name new-name

# 强制重命名(覆盖已有分支名)
$ git branch -M new-name

4.2.5 删除分支

# 删除已合并的分支
$ git branch -d feature-login

# 强制删除(即使未合并)
$ git branch -D feature-login

# 删除远程分支
$ git push origin --delete feature-login
# 或
$ git push origin :feature-login

⚠️ 注意:不能删除当前所在的分支,需要先切换到其他分支。


4.3 合并(Merge)

4.3.1 快进合并(Fast-Forward)

当目标分支是当前分支的直接后继时,Git 只需移动指针:

合并前:
main:    C1 → C2
                  ↘
feature:           C3 → C4

合并后(快进):
main:    C1 → C2 → C3 → C4
$ git switch main
$ git merge feature
Updating abc1234..def5678
Fast-forward
 src/login.js | 50 +++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

4.3.2 三方合并(Three-Way Merge)

当两个分支有分叉时,Git 创建一个新的合并提交

合并前:
          C4 (feature)
         ↗
C1 → C2 → C3 (main)

合并后:
          C4 ──────┐
         ↗         ↘
C1 → C2 → C3 → C5 (merge commit)
$ git switch main
$ git merge feature
Merge made by the 'ort' strategy.
 src/login.js | 50 +++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

# 合并时指定提交信息
$ git merge feature -m "Merge feature-login into main"

# 不自动提交(先审查合并结果)
$ git merge --no-commit feature

# 禁止快进合并(总是创建合并提交)
$ git merge --no-ff feature

# 仅允许快进合并(如果有分叉则失败)
$ git merge --ff-only feature

4.3.3 合并策略

策略命令参数适用场景
ort默认通用场景,递归三方合并
ours-s ours丢弃被合并分支的内容
theirs-X theirs保留被合并分支的内容
subtree-s subtree合并子目录项目
# 使用 ours 策略(只保留当前分支内容,但记录合并历史)
$ git merge -s ours feature

# 解决冲突时偏好被合并分支
$ git merge -X theirs feature

# 解决冲突时偏好当前分支
$ git merge -X ours feature

4.4 变基(Rebase)

变基将当前分支的提交"重放"到目标分支之上,产生线性历史。

变基前:
          C4 → C5 (feature)
         ↗
C1 → C2 → C3 (main)

变基后(在 feature 上执行 git rebase main):
                    C4' → C5' (feature)
                   ↗
C1 → C2 → C3 (main)
$ git switch feature
$ git rebase main
Successfully rebased and updated refs/heads/feature.

# 变基后切回 main 合并(快进合并)
$ git switch main
$ git merge feature
Updating abc1234..def5678
Fast-forward

Merge vs Rebase 对比

特性MergeRebase
历史记录保留分支结构线性历史
合并提交创建 merge commit无额外提交
安全性不修改历史重写历史
冲突处理一次性解决逐个提交解决
适用场景公共分支个人分支
可追溯性

⚠️ 黄金法则:不要对已经推送到远程的公共分支执行 rebase!


4.5 Cherry-Pick

Cherry-pick 将某个特定提交应用到当前分支:

# 拣选单个提交
$ git cherry-pick abc1234

# 拣选多个提交
$ git cherry-pick abc1234 def5678 ghi9012

# 拣选提交范围(不包含起始提交)
$ git cherry-pick abc1234..ghi9012

# 拣选提交范围(包含起始提交)
$ git cherry-pick abc1234^..ghi9012

# 只暂存不提交
$ git cherry-pick --no-commit abc1234

# 保留原始作者信息
$ git cherry-pick -x abc1234    # 添加 "cherry picked from" 注释

适用场景

  • 将 bugfix 从开发分支应用到发布分支
  • 从一个功能分支移植特定提交到另一个分支
  • 热修复(hotfix)应用

4.6 解决合并冲突

当两个分支修改了同一文件的同一区域时,会产生冲突。

冲突标记

$ git merge feature
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.

冲突文件内容:

<<<<<<< HEAD
const API_URL = "https://api.production.com";
=======
const API_URL = "https://api.staging.com";
>>>>>>> feature
标记含义
<<<<<<< HEAD当前分支的内容开始
=======分隔符
>>>>>>> feature被合并分支的内容结束

解决冲突步骤

# 1. 查看冲突文件
$ git status
Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   src/config.js

# 2. 编辑冲突文件(手动选择或合并内容)
$ vim src/config.js
# 修改为:
const API_URL = "https://api.production.com";

# 3. 标记为已解决
$ git add src/config.js

# 4. 完成合并
$ git commit -m "Merge feature, resolve config conflict"

使用合并工具

# 启动配置的合并工具
$ git mergetool

# 使用 VS Code
$ git mergetool --tool=vscode

使用 git checkout --ours/--theirs

# 保留当前分支版本
$ git checkout --ours src/config.js
$ git add src/config.js

# 使用被合并分支版本
$ git checkout --theirs src/config.js
$ git add src/config.js

4.7 分支管理最佳实践

分支命名规范

类型/简短描述

示例:
feature/user-authentication
feature/payment-integration
bugfix/login-timeout
hotfix/security-patch
release/v1.2.0
chore/update-dependencies
前缀用途生命周期
feature/新功能功能完成后合并
bugfix/Bug 修复修复后合并
hotfix/生产环境紧急修复立即合并
release/发布准备发布后合并
chore/杂项完成后合并
experiment/实验性代码验证后删除或合并

分支保护规则(GitHub/GitLab)

规则说明
禁止直接推送必须通过 PR/MR
要求代码审查至少 N 人批准
要求 CI 通过所有检查必须绿灯
禁止 force push防止历史被重写
要求线性历史禁止 merge commit

业务场景

场景推荐方案
日常功能开发git switch -c feature/xxx 开发完成后合并
紧急线上修复git switch -c hotfix/xxx 从 main 拉出,修复后合并
版本发布git switch -c release/v1.0 测试后合并到 main 并打标签
接收他人修复git cherry-pick <commit>
整理个人分支历史git rebase -i main 压缩提交
长期并行开发主开发分支 + 功能分支,定期 rebase

扩展阅读


🔗 上一章03 - 基础操作 | 下一章05 - 远程仓库