Git Merge
合并是 Git 中将不同分支的更改整合到一起的核心操作。本章将详细介绍各种合并策略、冲突解决和最佳实践。
合并基础概念
什么是合并?
合并是将一个分支的更改集成到另一个分支的过程。Git 提供了多种合并策略来处理不同的场景。
合并前:
A---B---C (main)
\
D---E (feature)
合并后:
A---B---C---F (main)
\ /
D---E (feature)合并的类型
- 快进合并 (Fast-forward):目标分支没有新提交
- 三方合并 (Three-way merge):两个分支都有新提交
- 压缩合并 (Squash merge):将多个提交压缩为一个
基本合并操作
准备演示环境
bash
# 创建演示项目
mkdir git-merge-demo
cd git-merge-demo
git init
# 创建初始提交
echo "# 合并演示项目" > README.md
git add README.md
git commit -m "初始提交"
# 创建并切换到功能分支
git checkout -b feature/user-login
echo "登录功能" > login.js
git add login.js
git commit -m "添加登录功能"
echo "登录样式" > login.css
git add login.css
git commit -m "添加登录样式"快进合并
当目标分支(通常是 main)没有新的提交时,Git 会进行快进合并:
bash
# 切换回主分支
git checkout main
# 执行快进合并
git merge feature/user-login输出:
Updating a1b2c3d..e4f5g6h
Fast-forward
login.css | 1 +
login.js | 1 +
2 files changed, 2 insertions(+)
create mode 100644 login.css
create mode 100644 login.js快进合并的特点:
- 不创建新的合并提交
- 历史保持线性
- 分支指针直接移动
禁用快进合并
bash
# 强制创建合并提交
git merge --no-ff feature/user-login
# 这会创建一个合并提交,即使可以快进三方合并
创建三方合并场景
bash
# 在主分支上添加提交
git checkout main
echo "主分支更新" >> README.md
git add README.md
git commit -m "更新 README"
# 创建新的功能分支
git checkout -b feature/user-register
echo "注册功能" > register.js
git add register.js
git commit -m "添加注册功能"
# 切换回主分支并合并
git checkout main
git merge feature/user-registerGit 会自动创建合并提交:
Merge made by the 'recursive' strategy.
register.js | 1 +
1 file changed, 1 insertion(+)
create mode 100644 register.js自定义合并提交信息
bash
# 合并时指定提交信息
git merge feature/user-register -m "合并用户注册功能"
# 合并时编辑提交信息
git merge feature/user-register --edit合并策略
递归策略 (Recursive)
这是 Git 的默认合并策略,适用于大多数情况:
bash
# 显式指定递归策略
git merge -s recursive feature/branch-name
# 递归策略的选项
git merge -s recursive -X ours feature/branch-name # 冲突时优先使用当前分支
git merge -s recursive -X theirs feature/branch-name # 冲突时优先使用合并分支
git merge -s recursive -X ignore-space-change feature/branch-name # 忽略空白字符变化Octopus 策略
用于合并多个分支:
bash
# 同时合并多个分支
git merge branch1 branch2 branch3Ours 策略
保留当前分支的内容,忽略其他分支的更改:
bash
# 使用 ours 策略
git merge -s ours feature/branch-nameSubtree 策略
用于子树合并:
bash
# 子树合并
git merge -s subtree feature/branch-name处理合并冲突
识别冲突
当两个分支修改了同一文件的同一部分时,会产生冲突:
bash
# 创建冲突场景
git checkout main
echo "主分支的配置" > config.txt
git add config.txt
git commit -m "添加主分支配置"
git checkout -b feature/config-update
echo "功能分支的配置" > config.txt
git add config.txt
git commit -m "更新配置文件"
# 尝试合并(会产生冲突)
git checkout main
git merge feature/config-update冲突输出:
Auto-merging config.txt
CONFLICT (content): Merge conflict in config.txt
Automatic merge failed; fix conflicts and then commit the result.查看冲突状态
bash
# 查看冲突状态
git status
# 查看冲突文件
git diff
# 查看冲突的详细信息
git diff --name-only --diff-filter=U冲突标记解释
冲突文件会包含特殊标记:
<<<<<<< HEAD
主分支的配置
=======
功能分支的配置
>>>>>>> feature/config-update标记说明:
<<<<<<< HEAD:当前分支的内容开始=======:分隔符>>>>>>> branch-name:合并分支的内容结束
手动解决冲突
bash
# 编辑冲突文件,选择保留的内容
echo "合并后的配置" > config.txt
# 标记冲突已解决
git add config.txt
# 完成合并
git commit使用合并工具
bash
# 启动默认合并工具
git mergetool
# 使用特定合并工具
git mergetool --tool=vimdiff
git mergetool --tool=vscode
# 配置默认合并工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'冲突解决策略
bash
# 选择当前分支的版本
git checkout --ours conflicted_file.txt
git add conflicted_file.txt
# 选择合并分支的版本
git checkout --theirs conflicted_file.txt
git add conflicted_file.txt
# 查看不同版本
git show :1:conflicted_file.txt # 共同祖先版本
git show :2:conflicted_file.txt # 当前分支版本
git show :3:conflicted_file.txt # 合并分支版本高级合并操作
压缩合并 (Squash Merge)
将功能分支的所有提交压缩为一个提交:
bash
# 压缩合并
git merge --squash feature/user-profile
# 需要手动提交
git commit -m "添加用户资料功能"压缩合并的特点:
- 保持主分支历史简洁
- 丢失功能分支的详细历史
- 适合功能完整后的合并
部分合并
bash
# 只合并特定文件
git checkout feature/branch-name -- specific_file.txt
git commit -m "合并特定文件"
# 合并特定提交
git cherry-pick commit_hash合并特定提交范围
bash
# 合并一系列提交
git cherry-pick start_commit..end_commit
# 合并多个不连续的提交
git cherry-pick commit1 commit2 commit3撤销合并
撤销未推送的合并
bash
# 撤销最后一次合并
git reset --hard HEAD~1
# 撤销合并但保留工作区更改
git reset --soft HEAD~1
# 撤销合并但保留暂存区
git reset --mixed HEAD~1撤销已推送的合并
bash
# 创建反向合并提交
git revert -m 1 merge_commit_hash
# -m 1 表示保留第一个父提交(通常是主分支)
# -m 2 表示保留第二个父提交(通常是功能分支)查找合并提交
bash
# 查看合并历史
git log --merges
# 查看特定合并的详细信息
git show merge_commit_hash
# 查看合并引入的更改
git diff merge_commit_hash^1 merge_commit_hash合并最佳实践
1. 合并前的准备
bash
# 确保工作区干净
git status
# 更新目标分支
git checkout main
git pull origin main
# 确保功能分支是最新的
git checkout feature/branch-name
git rebase main # 或者 git merge main2. 选择合适的合并策略
bash
# 小功能或修复:快进合并
git merge feature/small-fix
# 大功能:非快进合并,保留分支历史
git merge --no-ff feature/major-feature
# 实验性功能:压缩合并
git merge --squash feature/experimental3. 编写清晰的合并信息
bash
# 好的合并信息示例
git merge feature/user-auth -m "
合并用户认证功能
- 添加登录/注册页面
- 实现 JWT 认证
- 添加用户权限管理
- 更新相关测试
Closes #123
"4. 合并后的清理
bash
# 删除已合并的本地分支
git branch -d feature/merged-branch
# 删除远程分支
git push origin --delete feature/merged-branch
# 清理远程分支引用
git remote prune origin合并工作流
GitHub Flow
bash
# 1. 从 main 创建功能分支
git checkout -b feature/new-feature main
# 2. 开发并提交
git add .
git commit -m "实现新功能"
# 3. 推送分支
git push origin feature/new-feature
# 4. 创建 Pull Request
# 5. 代码审查
# 6. 合并到 main
git checkout main
git merge --no-ff feature/new-feature
# 7. 清理分支
git branch -d feature/new-feature
git push origin --delete feature/new-featureGit Flow
bash
# 功能开发流程
git checkout develop
git checkout -b feature/new-feature
# 开发完成后
git checkout develop
git merge --no-ff feature/new-feature
git branch -d feature/new-feature
# 发布流程
git checkout -b release/1.0.0 develop
# 修复发布相关问题
git checkout main
git merge --no-ff release/1.0.0
git tag v1.0.0
git checkout develop
git merge --no-ff release/1.0.0
git branch -d release/1.0.0合并工具和技巧
配置合并工具
bash
# VS Code
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# Vim
git config --global merge.tool vimdiff
# Beyond Compare
git config --global merge.tool bc3
git config --global mergetool.bc3.path "C:/Program Files/Beyond Compare 3/bcomp.exe"
# P4Merge
git config --global merge.tool p4merge
git config --global mergetool.p4merge.path "C:/Program Files/Perforce/p4merge.exe"合并别名
bash
# 设置有用的别名
git config --global alias.mg merge
git config --global alias.mgn "merge --no-ff"
git config --global alias.mgs "merge --squash"
# 查看合并历史的别名
git config --global alias.merges "log --merges --oneline"
# 撤销合并的别名
git config --global alias.unmerge "reset --hard HEAD~1"自动化合并脚本
bash
#!/bin/bash
# 安全合并脚本
merge_feature() {
local feature_branch=$1
local target_branch=${2:-main}
if [ -z "$feature_branch" ]; then
echo "用法: merge_feature <feature-branch> [target-branch]"
return 1
fi
# 检查工作区状态
if ! git diff-index --quiet HEAD --; then
echo "错误: 工作区有未提交的更改"
return 1
fi
# 更新目标分支
git checkout $target_branch
git pull origin $target_branch
# 合并功能分支
if git merge --no-ff $feature_branch; then
echo "合并成功"
# 询问是否删除分支
read -p "是否删除功能分支 $feature_branch? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git branch -d $feature_branch
git push origin --delete $feature_branch 2>/dev/null
fi
else
echo "合并失败,请解决冲突后手动完成"
return 1
fi
}
# 使用方法
merge_feature feature/user-auth故障排除
常见合并问题
bash
# 问题1: 合并中断
# 查看状态
git status
# 继续合并
git merge --continue
# 取消合并
git merge --abort
# 问题2: 合并工具无法启动
# 检查配置
git config --get merge.tool
# 重新配置
git config --global merge.tool vimdiff
# 问题3: 二进制文件冲突
# 选择一个版本
git checkout --ours binary_file.jpg
git checkout --theirs binary_file.jpg
git add binary_file.jpg合并性能优化
bash
# 对于大型仓库,使用合并缓存
git config --global merge.renameLimit 999999
# 启用并行合并
git config --global merge.tool.trustExitCode true
# 优化合并算法
git config --global merge.algorithm patience合并分析和报告
合并统计
bash
# 查看合并统计
git log --merges --stat
# 查看合并趋势
git log --merges --since="1 month ago" --pretty=format:"%ad" --date=short | sort | uniq -c
# 分析合并冲突
git log --grep="conflict" --oneline生成合并报告
bash
#!/bin/bash
# 合并分析报告
echo "=== 合并分析报告 ==="
echo "生成时间: $(date)"
echo ""
echo "=== 合并统计 ==="
echo "总合并次数: $(git log --merges --oneline | wc -l)"
echo "最近30天合并: $(git log --merges --since='30 days ago' --oneline | wc -l)"
echo ""
echo "=== 最近的合并 ==="
git log --merges --oneline -10
echo ""
echo "=== 合并作者统计 ==="
git log --merges --pretty=format:"%an" | sort | uniq -c | sort -rn总结
Git 合并的核心要点:
基本操作
bash
git merge branch-name # 基本合并
git merge --no-ff branch # 非快进合并
git merge --squash branch # 压缩合并
git merge --abort # 取消合并冲突处理
- 🔍 识别冲突文件和位置
- ✏️ 手动编辑或使用合并工具
- ✅ 标记冲突已解决并提交
- 🚫 必要时取消合并
最佳实践
- ✅ 合并前确保分支是最新的
- ✅ 选择合适的合并策略
- ✅ 编写清晰的合并信息
- ✅ 合并后及时清理分支
工作流选择
- 简单项目:直接合并
- 团队协作:Pull Request 流程
- 复杂项目:Git Flow 或 GitHub Flow
掌握合并操作后,你就能够:
- 🔄 安全地整合不同分支的更改
- 🛠️ 有效处理合并冲突
- 📈 维护清晰的项目历史
- 👥 支持团队协作开发
在下一章中,我们将学习 Git Flow 工作流程。