Skip to content
Git
分支与合并

分支与合并

分支是 Git 的核心能力。轻量级的分支模型让你可以高效地进行并行开发。


分支操作

查看分支

git branch                     # 列出本地分支
git branch -a                  # 列出本地 + 远程分支
git branch -r                  # 仅远程分支
git branch -v                  # 显示每个分支的最后一次提交
git branch -vv                 # 显示本地分支与远程的追踪关系
git branch --merged            # 已合并到当前分支的分支
git branch --no-merged         # 尚未合并的分支

创建与切换

# 创建分支
git branch <name>              # 在当前 HEAD 处创建
git branch <name> <ref>        # 在指定 ref 处创建

# 切换分支
git checkout <name>            # 旧语法
git switch <name>              # 新语法(Git 2.23+)

# 创建并切换
git checkout -b <name>         # 旧语法
git switch -c <name>           # 新语法

# 基于远程分支创建
git checkout -b <local> origin/<remote>

💡 checkout vs switchgit checkout 功能过载(切换分支 + 恢复文件),Git 2.23 引入了 git switch(切换分支)和 git restore(恢复文件),职责更清晰。

删除分支

git branch -d <name>           # 安全删除(已合并才允许)
git branch -D <name>           # 强制删除
git push origin --delete <name>  # 删除远程分支

重命名分支

git branch -m <new_name>       # 重命名当前分支
git branch -m <old> <new>      # 重命名指定分支

强制移动分支

# 让分支强制指向某提交(改写历史)
git branch -f <branch_name> <ref>
# 例:git branch -f main HEAD~3  将 main 强制移到 HEAD 往前 3 个

🚨 陷阱branch -f 是改写历史,仅在本地分支上使用,绝对不要在已推送的共享分支上执行。


merge(合并)

合并后保留两个分支的 parent 链,形成合并提交。

# 将 branch_name 合并到当前分支
git merge <branch_name>

# 创建合并提交(即使可以 fast-forward 也要生成 merge commit)
git merge --no-ff <branch_name>

# 快进合并(如果可以的话,不产生 merge commit)
git merge --ff-only <branch_name>

# 压缩合并(将被合并分支的所有提交压缩为一个,不保留历史)
git merge --squash <branch_name>
# 然后 git commit

合并策略示意

# Fast-forward(默认,当目标分支没有分叉时)
main:    A---B---C
feature:         D---E
结果:    A---B---C---D---E   (main 指针直接移到 E)

# 三方合并(有分叉时)
main:    A---B---C---D
                \
feature:         E---F
结果:    A---B---C---D---M   (创建 merge commit M)
                \     /
                 E---F

# --no-ff(强制创建 merge commit)
main:    A---B
                \
feature:         C---D
结果:    A---B-------M       (强制产生 M,保留分支历史)
                \   /
                 C---D
策略 产生 merge commit 保留分支历史 适用场景
Fast-forward 分支痕迹消失 小型分支,无关紧要
--no-ff 完整可见 feature 分支(推荐)
--squash 压缩为单提交 提交混乱的分支

rebase(变基)

🔬 深入原理:rebase 不是"移动"提交,而是复制提交到新的 base 上。原来的提交在 git gc 之前仍然存在,通过 reflog 可找回。

# 将当前分支变基到目标分支
git rebase <target_branch>
# 等效于:把当前分支的独有提交"搬到"target_branch 的最新提交之后

# 示例
git checkout feature
git rebase main                # 把 feature 的提交放到 main 后面

变基过程

变基前:
main:    A---B---C---D
                \
feature:         E---F

执行 git rebase main:
                ┌→ E'---F'   ← feature 的新位置
                │
main:    A---B---C---D
                \
feature:         E---F        ← 旧提交仍在,但分支指针已移走

交互式 rebase(-i)

交互式 rebase 是最强大的提交整理工具:

git rebase -i <ref>            # 编辑从当前到 ref 的所有提交

# 常用操作:
git rebase -i HEAD~3           # 整理最近 3 个提交
git rebase -i <branch>         # 整理当前分支独有的提交

在打开的编辑器中,每条提交前面的 pick 可替换为:

命令 缩写 作用
pick p 保留此提交
reword r 修改提交信息
edit e 暂停,让你修改提交内容
squash s 合并到上一个提交,保留信息
fixup f 合并到上一个提交,丢弃信息
drop d 删除此提交
break b 在此处暂停执行
# 示例:将最近 3 个提交合并为一个
git rebase -i HEAD~3
# 编辑器中将后两个 pick → squash

# 重新排序:在编辑器中调整行的顺序即可

💡 最佳实践:在推送 feature 分支前,用 git rebase -i 整理提交历史(squash 修复性提交、整理提交顺序)。

解决 rebase 冲突

# 发生冲突后
# 1. 手动解决冲突文件
# 2.
git add <resolved_files>
git rebase --continue

# 跳过当前提交
git rebase --skip

# 放弃整个 rebase
git rebase --abort

merge vs rebase

merge rebase
历史记录 完整,有分叉 线性,清晰
冲突解决 一次性解决 每个提交逐个解决
追溯能力 强(保留原始上下文) 弱(历史被改写)
远程分支 安全 危险(改写历史)
推荐场景 合并 feature → main 同步 main → feature

🚨 黄金规则永远不要 rebase 已经推送到共享仓库的提交! rebase 会改写提交 hash,导致协作者的仓库与你产生冲突。rebase 仅限于本地尚未推送的提交。


cherry-pick(精选)

将指定的提交"复制"到当前分支:

git cherry-pick <hash1> <hash2> ...
git cherry-pick <hash1>..<hash4>    # 范围(不含 hash1)
git cherry-pick <hash1>^..<hash4>   # 范围(含 hash1)

# 发生冲突时
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --skip
原状态:
main:    A---B---C---D
feature:     E---F---G

# git checkout main; git cherry-pick F G
结果:
main:    A---B---C---D---F'---G'
feature:     E---F---G

合并冲突解决

冲突发生

git merge feature
# Auto-merging main.go
# CONFLICT (content): Merge conflict in main.go
# Automatic merge failed; fix conflicts and then commit.

冲突文件内容

<<<<<<< HEAD (当前分支的修改)
    fmt.Println("hello from main")
=======
    fmt.Println("hello from feature")
>>>>>>> feature (被合并分支的修改)

解决步骤

# 1. 编辑文件,删除标记,保留正确代码

# 2. 标记为已解决
git add <file>

# 3. 继续
git merge --continue       # merge
git rebase --continue      # rebase
git cherry-pick --continue # cherry-pick

# 放弃合并
git merge --abort

冲突解决工具

# 使用 mergetool
git mergetool

# VSCode 内置三路合并视图,推荐直接使用
# 命令行查看冲突状态
git diff --name-only --diff-filter=U   # 列出冲突文件

选择策略

# 冲突时全部采纳某一方的修改
git checkout --ours <file>     # 采纳当前分支
git checkout --theirs <file>   # 采纳合并分支
git add <file>

分支策略选择

策略 描述 适用团队
直接 merge 保留完整分支历史 需要完整追溯的团队
rebase + merge feature 分支 rebase 到 main 后再 merge 追求线性历史
squash merge 将整个 feature 压缩为一个提交 提交不规范的团队
rebase 推送 本地 rebase 后 force push(仅自己分支) 个人分支整理