辨析git reset三种模式以及和git revert的区别:回退到指定版本和撤销指定版本的操作

发布于:2025-07-16 ⋅ 阅读:(12) ⋅ 点赞:(0)

git revert:撤销指定版本的修改内容

git revert 不是让的项目“回到”版本C的状态
作用是:创建一个全新的提交,这个新提交的内容,刚好是版本C所做修改的“反向操作”

实例介绍

假设项目是一个故事文件 story.txt,历史如下:

  1. Commit A (初始)

    • 文件内容:
      从前有座山。
      
  2. Commit B (发展)

    • 增加了新的一行。
    • 文件内容:
      从前有座山。
      山里有座庙。
      
  3. Commit C (错误的修改)

    • 画蛇添足,加了一句不该加的话。
    • 文件内容:
      从前有座山。
      山里有座庙。
      【庙被拆了。】  <-- 这是Commit C引入的修改
      
  4. Commit D (后续的工作)

    • 没意识到C的错误,继续往下写。
    • 文件内容:
      从前有座山。
      山里有座庙。
      【庙被拆了。】
      和尚下山了。   <-- 这是Commit D引入的修改
      

现在的项目历史是 A -> B -> C -> D


执行 git revert C

目标:希望撤销“庙被拆了”这句错误的话,但必须保留“和尚下山了”这句有用的后续工作。

  1. 分析Commit C:Git会查看 Commit C 到底做了什么修改。它发现 C 的修改是**“增加了‘庙被拆了。’这一行”**。

  2. 执行反向操作revert 的核心就是做反向操作。既然 C 的操作是“增加”,那么它的反向操作就是**“删除‘庙被拆了。’这一行”**。

  3. 创建新Commit E:Git 会把这个“反向操作”作为一个全新的提交,我们叫它 Commit E。

    • 这个 Commit E 的提交信息通常会自动生成为 “Revert ‘Commit C 的提交信息’”。
    • 这个 Commit E 的修改内容,就是从当前最新版本(D的状态)中,删除 “庙被拆了。” 这一行。

revert之后的结果:

  • 新的项目历史: A -> B -> C -> D -> E (Revert C)
  • 最终的文件内容:
    从前有座山。
    山里有座庙。
    和尚下山了。
    

revert总结

  • 我们没有回到 Commit C 的状态(那会丢失“和尚下山了”)。
  • 我们也没有回到 Commit B 的状态(那同样会丢失“和尚下山了”)。
  • 我们只是在最新的状态(D)基础上,精确地“抵消”掉了 C 所带来的变更,同时完好无损地保留了 D 的变更。

git reset : 回退到指定版本

继续用这个生动的故事例子来解析 git reset C 会发生什么。这就像是坐上了时光机,但时光机有不同的模式,对应着 reset 的三个主要选项:--hard, --mixed (默认), 和 --soft

出发前的状态(和之前一样):

  • 项目历史: A -> B -> C -> D (HEAD指针在这里)
  • 当前文件 story.txt 内容:
    从前有座山。
    山里有座庙。
    【庙被拆了。】
    和尚下山了。
    

现在,我们执行 git reset C。这个操作的核心是:HEAD 指针和当前分支(比如 main)的指针,强行移动到 commit C。这就像是宣布:“官方历史只记录到 C 为止!”

commit D 并没有被立即删除,它只是变成了“孤魂野鬼”,不再被任何分支引用。如果后续没有其他操作,它最终会被Git的垃圾回收机制清理掉。

那么,你的工作区(story.txt 文件)和暂存区会发生什么变化呢?这取决于你使用的模式。


模式一:git reset --hard C (最暴力、最彻底的模式)

这就像是霸道总裁的时光机,不仅回到了过去,还把之后的一切痕迹都抹除了。

  • 操作git reset --hard C

  • 时光机的行为:

    1. 历史指针移动: HEAD 指针从 D 移动回 C。历史记录现在看起来是 A -> B -> C
    2. 暂存区重置: 暂存区(Staging Area)的内容被完全重置,使其与 C 的内容一模一样。
    3. 工作区重置: 你的工作目录(硬盘上的 story.txt 文件)被强制更新,使其内容也与 C 的内容一模一样。
  • 结果:

    • 最终历史: A -> B -> C (HEAD)
    • 最终文件 story.txt 内容:
      从前有座山。
      山里有座庙。
      【庙被拆了。】
      
    • 损失: “和尚下山了”这句在 D 中的修改,彻底消失了,无论是在暂存区还是在你的文件里,都找不到了。这是个有损操作,一定要小心!

模式二:git reset --mixed C (默认模式)

如果你只输入 git reset C,Git默认就是使用 --mixed 模式。这像是时光机把你带回去了,但把你之前写的草稿(工作成果)留在了桌子上。

  • 操作: git reset Cgit reset --mixed C

  • 时光机的行为:

    1. 历史指针移动: HEAD 指针从 D 移动回 C。历史记录现在是 A -> B -> C
    2. 暂存区重置: 暂存区的内容被重置,与 C 的内容保持一致。
    3. 工作区不变: 关键区别! 你的工作目录(story.txt 文件)保持不变,它仍然是 D 操作之后的样子。
  • 结果:

    • 最终历史: A -> B -> C (HEAD)
    • 最终文件 story.txt 内容:
      从前有座山。
      山里有座庙。
      【庙被拆了。】
      和尚下山了。
      
    • git status 的状态: Git会告诉你,你的工作目录和暂存区有差异。它会显示“和尚下山了”这句是未暂存的修改 (unstaged change)
    • 意义: Git帮你撤销了 commit D 这个提交行为,但保留了 D 的代码修改。你可以检查这些修改,决定是重新提交、修改后再提交,还是彻底丢弃 (git checkout -- .)。

模式三:git reset --soft C (最温柔的模式)

这就像是时光机仅仅移动了书签,而书的内容和你的草稿都原封不动。

  • 操作: git reset --soft C

  • 时光机的行为:

    1. 历史指针移动: 这是它唯一做的事情! HEAD 指针从 D 移动回 C。历史记录现在是 A -> B -> C
    2. 暂存区不变: 暂存区的内容保持不变,和 D 操作后一样。
    3. 工作区不变: 你的工作目录(story.txt 文件)也保持不变
  • 结果:

    • 最终历史: A -> B -> C (HEAD)
    • 最终文件 story.txt 内容:
      从前有座山。
      山里有座庙。
      【庙被拆了。】
      和尚下山了。
      
    • git status 的状态: Git会告诉你,你有已暂存的修改 (staged changes),这个修改就是 D 所做的 “和尚下山了”。
    • 意义: 这个模式非常适合用于“合并提交”。比如你发现 CD 两个提交其实应该合成一个,你就可以 reset --soft B,然后把 CD 的所有修改一次性重新提交。

reset总结

操作 (git reset C) 历史指针 (HEAD) 暂存区 (Staging Area) 工作目录 (文件) 主要用途
--hard 回到 C 变成 C 的样子 变成 C 的样子 彻底丢弃 C 之后的所有提交和修改,慎用!
--mixed (默认) 回到 C 变成 C 的样子 保持不变 撤销提交但保留代码修改,可以重新整理。
--soft 回到 C 保持不变 保持不变 合并多个提交,或只是想修改最后一次提交的信息。

所以,git reset C 确实是“回到过去”,但“回到过去”之后现场会变成什么样,取决于你选择的时光机模式。这与 git revert 那种“向前走,做反向操作来弥补”的思路,有着本质的区别。

总结

git revert 像是在故事的最新结尾(“和尚下山了”之后)补写了一个新章节,内容是“哦,之前说的庙被拆了是假的”,从而优雅地修正了错误,保留了所有历史。

git reset 则是直接乘坐时光机回到“庙被拆了”那一刻,然后粗暴地撕掉了后面“和尚下山了”这一页历史,让故事看起来就像从未发生过一样。


网站公告

今日签到

点亮在社区的每一天
去签到