分支与合并
分支是 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 switch:
git 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 --abortmerge 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(仅自己分支) | 个人分支整理 |