Git 实战:如何优雅地撤销历史中的某一个 Commit?

这是一个非常经典且实用的 Git 操作场景。为了帮你写好这篇博客文档,我将它设计成一篇**技术教程(Tutorial)**的风格。

你可以直接复制下面的 Markdown 内容发布到你的博客(如 CSDN, 掘金, GitHub Pages 等)。


Git 实战:如何优雅地撤销历史中的某一个 Commit?

在日常开发中,我们经常遇到这样的情况:刚提交了几个 Commit,突然发现倒数第 3 个(最旧的那个)Commit 里包含了一个严重的 Bug,或者不小心提交了敏感文件(如密码)。

此时,如果你只想撤销这特定的一条提交,而保留后面提交的代码,该怎么办?

本文将介绍两种最常用的方法:安全法(Revert)洁癖法(Rebase)

场景复现

假设我们当前的 Git 提交历史如下(从上往下看,C 是最新的提交,A 是你想删掉的旧提交):

text 复制代码
commit C (HEAD -> master)  <-- 最新的提交
commit B
commit A                   <-- 【我想撤销这一个】

我们的目标是:干掉 A,但保留 B 和 C 的代码修改。

获取 Commit ID(哈希值)是第一步,请运行:

bash 复制代码
git log --oneline

假设输出如下:

text 复制代码
3f8a2c1 (HEAD) feat: 完成功能 C
2b9e1d4 feat: 完成功能 B
1a7c3b2 fix: 修复了 Bug A (这个提交有问题,要撤销)

方法一:安全法 git revert(推荐)

如果你是在团队协作 的分支(如 developmaster)上操作,或者你的代码已经推送到远程仓库(Remote),请务必使用这个方法

原理

git revert 不会删除历史记录,而是会创建一个新的 Commit,这个新 Commit 的内容正好是把目标 Commit 的修改"反着做一遍"。

操作步骤

  1. 执行撤销命令

    找到你想撤销的 Commit ID(例如 1a7c3b2),运行:

    bash 复制代码
    git revert 1a7c3b2
  2. 处理冲突(如果有)

    如果后面的 Commit B 或 C 修改了和 Commit A 同一行代码,Git 会提示冲突。

    • 打开冲突文件,手动保留你需要的代码。
    • git add <冲突文件>
    • git revert --continue
  3. 保存提交信息

    Git 会自动弹出一个编辑器让你写 Commit Message,默认通常是 Revert "fix: 修复了 Bug A"。保存并退出即可。

结果

现在的历史记录变成了:

text 复制代码
commit D (HEAD) <-- 新的提交:撤销了 A 的修改
commit C
commit B
commit A        <-- A 依然存在历史中,但效果被 D 抵消了

优点: 历史记录清晰真实,不会破坏同事的代码库。


方法二:洁癖法 git rebase -i(慎用)

如果你是在本地分支 (Local Branch)自己开发,代码还没有 push 到远程,或者你有强迫症想要一个完美的线性历史,可以使用交互式变基。

原理

时光倒流,把历史记录修改一遍,直接把 A 从历史上抹去,然后把 B 和 C 重新"播放"一遍。

操作步骤

  1. 开始交互式变基

    我们要撤销的是倒数第 3 个,所以我们要回溯到它的父节点(即前 3 个):

    bash 复制代码
    git rebase -i HEAD~3
  2. 编辑指令

    终端会弹出一个 Vim/Nano 编辑器界面,显示如下内容:

    text 复制代码
    pick 1a7c3b2 fix: 修复了 Bug A
    pick 2b9e1d4 feat: 完成功能 B
    pick 3f8a2c1 feat: 完成功能 C

    你需要把你想删除的那个 Commit 前面的 pick 改为 drop(或者直接删除那一行):

    text 复制代码
    drop 1a7c3b2 fix: 修复了 Bug A  <-- 改成 drop
    pick 2b9e1d4 feat: 完成功能 B
    pick 3f8a2c1 feat: 完成功能 C

    保存并退出编辑器(Vim 操作是 :wq)。

  3. 处理冲突

    因为 B 和 C 是基于 A 开发的,如果删除了 A,B 和 C 可能会找不到依赖,从而产生冲突。

    • 解决冲突。
    • git add .
    • git rebase --continue

结果

现在的历史记录变成了:

text 复制代码
commit C' (HEAD) <-- 哈希值变了
commit B'        <-- 哈希值变了
                 <-- A 彻底消失了,仿佛从未发生过

⚠️ 警告:

如果你的代码之前已经 Push 到了远程仓库,使用此方法后,你需要使用 git push --force 才能推送到远程。这可能会覆盖队友的提交,在多人协作分支上严禁使用!


总结

维度 git revert git rebase -i (drop)
操作性质 新增一个"反向"提交 修改历史,直接删除提交
安全性 (适合公共分支) (仅限本地未公开分支)
历史记录 会变长 (A -> B -> C -> Revert A) 会变短 (B -> C)
是否需要强推 不需要 需要 (push -f)

一句话建议:

只要代码已经 Push 过了,或者这不仅仅是你一个人的分支,请无脑选择 git revert


希望这篇文档能帮到你!如果你觉得有用,别忘了点个赞哦。

相关推荐
啥都不懂的小小白5 小时前
Git 入门指南:从零开始掌握版本控制
git
AI逐月5 小时前
Git 彻底清除历史记录
大数据·git·elasticsearch
有什么东东6 小时前
Windows安装git教程以及初步使用
git
不爱吃米饭_6 小时前
Gitea 轻量级的Git方案 - Gitlab的替代品
git·gitlab·gitea
CoderJia程序员甲9 小时前
GitHub 热榜项目 - 日榜(2025-12-15)
git·ai·开源·llm·github
大柏怎么被偷了9 小时前
【Git】远程操作
git
studytosky10 小时前
Linux 基础开发工具(3):Git 控制与 GDB 调试实用指南
linux·运维·服务器·网络·数据库·git
云闲不收12 小时前
AI编程系列——git-worktree并行开发
git·ai编程
秦时明月天明12 小时前
GitLab SSH Key 过期:git pull failed : remote your ssh key has expired
git·ssh·gitlab