一:已经推送到远程仓库
1. 使用 git revert(安全方案)
这会创建一个新的提交来撤销合并,不修改历史:
# 查看合并提交的 ID
git log --oneline
# 撤销合并提交(-m 1 表示保留当前分支的修改)
git revert -m 1 <合并提交的commit-id>
# 推送到远程
git push origin <分支名>
这里保留当前分支的修改是指:
合并提交的特殊性
普通的提交只有一个父提交,但合并提交(merge commit)有两个父提交:
bash
# 查看合并提交的父提交
git log --pretty=%P -1 <merge-commit-id>
# 输出示例:def5678 ghi9012
# 第一个父提交 (def5678) - 合并时的当前分支(main)
# 第二个父提交 (ghi9012) - 被合并进来的分支(feature)
-m 参数的作用
-m 1 告诉 Git:"撤销合并时,保留第一个父提交(当前分支)的内容"
bash
# 图示说明
# 合并前的状态:
A---B---C (main)
/
D---E---F---G (feature)
# 合并后:
A---B---C---M (main)
/ /
D---E---F-------G (feature)
# M 的父提交:
# - 父提交1: C (main分支的提交)
# - 父提交2: G (feature分支的提交)
-m 1 vs -m 2
bash
# 保留 main 分支的修改,丢弃 feature 的修改
git revert -m 1 M
# 保留 feature 分支的修改,丢弃 main 的修改
git revert -m 2 M
实际例子
bash
# 假设在 main 分支上合并了 feature 分支
git checkout main
git merge feature
# 想撤销合并,恢复 main 分支原来的样子
git revert -m 1 HEAD
更直观的理解
把合并提交想象成一个十字路口:
- -m 1:沿着原路返回(保留原分支)
- -m 2:沿着岔路返回(保留合并进来的分支)
bash
# 场景1:合并错了分支
git revert -m 1 # 撤销合并,回到合并前
# 场景2:想切换主分支到另一个分支
git revert -m 2 # 相当于把主分支变成被合并的分支
查看合并提交的父分支
bash
# 查看合并提交的详细信息
git show <merge-commit-id>
# 输出会显示:
# Merge: def5678 ghi9012
# def5678 是第一个父提交
# g
总结:-m 1 就是告诉 Git "撤销这次合并,但保留合并时当前分支的内容",这是最常用的撤销合并的方式。
二、如何在主分支上撤回所有功能分支A的合并提交
这是一个很常见的需求:主分支上合并了多个功能分支,现在只想撤回功能分支A的所有合并提交,但保留其他功能分支的修改。
核心挑战
功能分支A的提交可能:
- 多次合并到主分支(多次merge)
- 与其他分支的提交交错在一起
- 部分提交可能被其他分支依赖
方法一:使用 git revert + 查找所有合并提交(推荐)
bash
# 1. 找出功能分支A的所有合并提交
git log --oneline --merges main --grep="Merge branch 'feature-A'"
# 或者更精确地查找
git log --oneline --merges main | grep "feature-A"
# 2. 从新到旧依次撤销(注意顺序:从最新到最旧)
git revert -m 1 <最新的合并commit-A>
git revert -m 1 <次新的合并commit-A>
# ...
# 3. 推送
git push origin main