Skip to content

Git Merge

合并是 Git 中将不同分支的更改整合到一起的核心操作。本章将详细介绍各种合并策略、冲突解决和最佳实践。

合并基础概念

什么是合并?

合并是将一个分支的更改集成到另一个分支的过程。Git 提供了多种合并策略来处理不同的场景。

合并前:
A---B---C  (main)
     \
      D---E  (feature)

合并后:
A---B---C---F  (main)
     \     /
      D---E    (feature)

合并的类型

  1. 快进合并 (Fast-forward):目标分支没有新提交
  2. 三方合并 (Three-way merge):两个分支都有新提交
  3. 压缩合并 (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-register

Git 会自动创建合并提交:

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 branch3

Ours 策略

保留当前分支的内容,忽略其他分支的更改:

bash
# 使用 ours 策略
git merge -s ours feature/branch-name

Subtree 策略

用于子树合并:

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 main

2. 选择合适的合并策略

bash
# 小功能或修复:快进合并
git merge feature/small-fix

# 大功能:非快进合并,保留分支历史
git merge --no-ff feature/major-feature

# 实验性功能:压缩合并
git merge --squash feature/experimental

3. 编写清晰的合并信息

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-feature

Git 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 工作流程。

本站内容仅供学习和研究使用。