当前位置: 首页 > news >正文

Git 版本回退与撤销

文章目录

  • Git 版本回退与撤销:写错代码后如何优雅反悔
    • 一、为什么需要版本回退
    • 二、git reset 的三种模式
      • 1. --soft:只移动版本库指针
      • 2. --mixed:回退版本库和暂存区
      • 3. --hard:版本库、暂存区、工作区全部回退
    • 三、HEAD、commit id 和版本定位
      • 1. 使用 commit id
      • 2. 使用 HEAD
    • 四、实战:回退到旧版本,再找回新版本
      • 1. 回退到 version2
      • 2. 回退之后又想回到 version3 怎么办
      • 3. git log 和 git reflog 的区别
    • 五、撤销修改:按文件所在区域处理
      • 1. 只修改了工作区,还没有 add
      • 2. 已经 add,但还没有 commit
      • 3. 已经 commit
    • 六、删除文件:恢复误删与彻底删除
      • 1. 文件误删:恢复回来
      • 2. 文件确实不要了:从版本库删除
      • 3. rm 和 git rm 的区别
    • 七、常用撤销命令和风险提醒
      • 1. 回退版本
      • 2. 找回历史操作
      • 3. 丢弃工作区修改
      • 4. 撤销暂存区修改
      • 5. 删除文件
      • 6. reset 三种模式对照
    • 总结

Git 版本回退与撤销:写错代码后如何优雅反悔

前面已经整理了 Git 的本地仓库、工作区、暂存区、版本库,以及git addgit commitgit statusgit diff等基础命令。

从这一篇开始,进入 Git 非常重要的一类能力:反悔能力

写代码不可能永远一次写对,实际开发中经常会遇到这些情况:

  • 某次提交写错了,想回到上一个版本;
  • 工作区改乱了,想丢掉当前修改;
  • 已经git add了,但又不想提交这部分内容;
  • 已经commit了,想撤回这次提交;
  • 文件误删了,想恢复;
  • 文件确实不要了,想从版本库里删除。

这些场景都和 Git 的版本管理、撤销修改有关。

这一篇重点整理:

  • git reset如何回退版本;
  • --soft--mixed--hard有什么区别;
  • HEADHEAD^HEAD~n怎么理解;
  • git reflog如何找回历史操作;
  • 工作区、暂存区、版本库里的修改分别怎么撤销;
  • 删除文件后如何恢复或彻底删除。

一、为什么需要版本回退

Git 最重要的能力之一,就是可以记录项目历史。

既然能记录历史,就意味着我们可以在需要的时候回到某个历史版本。

比如一个文件ReadMe经过了多次提交:

add version1 add version2 add version3

此时如果发现version3写错了,想回到version2,就需要用到版本回退

Git 中用于版本回退的核心命令是:

gitreset

它的基本语法可以写成:

gitreset[--soft|--mixed|--hard][目标版本]

其中,目标版本可以是:

  • 某个完整的commit id
  • 某个简短的commit id
  • HEAD
  • HEAD^
  • HEAD~n

不过,git reset并不是简单地“把代码变回去”这么一句话就能解释完。

因为 Git 中有三个区域:

  • 工作区
  • 暂存区
  • 版本库

不同参数会影响不同区域。

所以理解git reset时,关键不是只记命令,而是要想清楚:

这次回退到底要影响版本库、暂存区,还是工作区?


二、git reset 的三种模式

git reset最常见的三个参数是:

  • --soft
  • --mixed
  • --hard

这三个参数的区别非常重要。

1. --soft:只移动版本库指针

--soft只会移动当前分支指针,也就是让版本库回到指定提交。

它不会修改暂存区,也不会修改工作区。

可以理解为:

版本库回退 暂存区不变 工作区不变

示意:

gitreset--softHEAD^

这种方式常用于:

刚提交完发现提交说明写错了,或者想把上一次提交拆开重新提交。

因为--soft不会丢掉修改,只是把提交撤回来,修改仍然保留在暂存区。

2. --mixed:回退版本库和暂存区

--mixedgit reset的默认模式。

也就是说,下面两条命令效果类似:

gitreset HEAD^gitreset--mixedHEAD^

--mixed会移动版本库指针,同时重置暂存区,但不会修改工作区。

可以理解为:

版本库回退 暂存区回退 工作区不变

这种方式常用于:

已经git add了,但想把暂存区里的内容撤出来,重新选择要提交的文件。

也就是说,--mixed可以把修改从暂存区退回工作区。

3. --hard:版本库、暂存区、工作区全部回退

--hard是最彻底,也最危险的模式。

它会同时回退:

  • 版本库;
  • 暂存区;
  • 工作区。

可以理解为:

版本库回退 暂存区回退 工作区也回退

示例:

gitreset--hardHEAD^

执行后,工作区中的文件内容也会变成目标版本的样子。

这意味着:

如果工作区里有还没提交的修改,使用--hard可能会直接丢掉这些修改。

所以--hard一定要慎用。

在真实开发中,执行下面这种命令前最好先确认:

gitstatusgitdiff

确认没有重要修改后,再考虑使用:

gitreset--hard目标版本


三、HEAD、commit id 和版本定位

回退版本时,必须告诉 Git 要回到哪里。

Git 中常见的版本定位方式有几种。

1. 使用 commit id

每一次提交都有一个唯一的commit id

可以通过:

gitlog--pretty=oneline

查看提交历史:

d95c13ffc878a55a25a3d04e22abfc7d2e3e1383(HEAD ->master)addversion3 14c12c32464d6ead7159f5c24e786ce450c899ddaddversion2 cff9d1e019333318156f8c7d356a78c9e49a6e7baddversion1

如果想回到version2,可以直接指定对应的提交 ID:

gitreset--hard14c12c32464d6ead7159f5c24e786ce450c899dd

Git 的commit id很长,但实际使用时通常不需要写完整。

只要前几位能唯一定位这次提交,就可以使用短 ID:

gitreset--hard14c12c3

2. 使用 HEAD

HEAD可以先简单理解为:

当前所在版本。

如果当前最新提交是version3,那么HEAD就指向version3

常见写法:

HEAD

表示当前版本。

HEAD^

表示上一个版本。

HEAD^^

表示上上一个版本。

不过在实际使用中,更推荐使用HEAD~n来表达往前数几个版本。

比如:

HEAD~0

表示当前版本。

HEAD~1

表示上一个版本。

HEAD~2

表示上上一个版本。

这里补充一个容易混淆的点:

在线性提交历史中,HEAD^^可以理解为上上个版本,但HEAD^2并不等价于HEAD~2

HEAD^2在 Git 中通常和合并提交的第二个父提交有关,后面学习分支合并时再展开。

所以普通版本回退时,想表示“往前退 n 个版本”,建议优先使用:

HEAD~n

比如回退到上一个版本:

gitreset--hardHEAD~1

回退到上上个版本:

gitreset--hardHEAD~2


四、实战:回退到旧版本,再找回新版本

下面用一个完整例子串起来。

假设ReadMe文件经历了三次提交:

# 第一次提交gitaddReadMegitcommit-m"add version1"# 第二次提交gitaddReadMegitcommit-m"add version2"# 第三次提交gitaddReadMegitcommit-m"add version3"

查看提交历史:

gitlog--pretty=oneline

输出类似:

d95c13ffc878a55a25a3d04e22abfc7d2e3e1383(HEAD ->master)addversion3 14c12c32464d6ead7159f5c24e786ce450c899ddaddversion2 cff9d1e019333318156f8c7d356a78c9e49a6e7baddversion1

当前最新版本是version3

1. 回退到 version2

如果发现version3写错了,希望工作区也回到version2,可以使用:

gitreset--hard14c12c32464d6ead7159f5c24e786ce450c899dd

或者使用短 ID:

gitreset--hard14c12c3

执行后可能会看到:

HEAD is now at 14c12c3addversion2

这说明当前版本已经回到了version2

此时查看文件内容:

catReadMe

会发现version3对应的内容已经不在了。

再查看日志:

gitlog--pretty=oneline

可能会看到:

14c12c32464d6ead7159f5c24e786ce450c899dd(HEAD ->master)addversion2 cff9d1e019333318156f8c7d356a78c9e49a6e7baddversion1

此时git log中已经看不到version3了。

2. 回退之后又想回到 version3 怎么办

问题来了:如果回退到version2后,又后悔了,想重新回到version3,怎么办?

如果还记得version3commit id,可以直接执行:

gitreset--hardd95c13f

但是实际开发中,终端输出可能早就被清掉了,git log里也看不到version3了。

这时可以使用:

gitreflog

git reflog会记录本地HEAD的移动历史。

示例输出:

14c12c3 HEAD@{0}: reset: moving to 14c12c3 d95c13f HEAD@{1}: commit:addversion3 14c12c3 HEAD@{2}: commit:addversion2 cff9d1e HEAD@{3}: commit:addversion1

这里可以看到version3的短提交 ID 是:

d95c13f

于是可以重新回到version3

gitreset--hardd95c13f

执行后:

HEAD is now at d95c13faddversion3

这样就找回来了。

3. git log 和 git reflog 的区别

git loggit reflog都能看历史,但关注点不同。

可以简单理解为:

git log :查看当前提交链上的历史提交 git reflog :查看 HEAD 在本地移动过的历史记录

如果一次提交已经不在当前提交链上,git log可能看不到。

但只要本地操作记录还在,git reflog仍然有机会找到它。

所以,当你回退版本后发现“之前那个 commit 找不到了”,不要慌,先试试:

gitreflog

五、撤销修改:按文件所在区域处理

撤销修改时,最重要的是先判断文件处在哪个区域。

同样是“撤销”,文件状态不同,命令也不同。

常见情况有三种:

  • 工作区修改了,但还没有git add
  • 已经git add,但还没有git commit
  • 已经git add,也已经git commit

不要一上来就乱敲reset --hard

更稳的思路是:

先 git status 看状态 再决定撤销工作区、暂存区,还是回退提交

1. 只修改了工作区,还没有 add

假设修改了ReadMe

vimReadMe

查看状态:

gitstatus

看到:

Changes not stagedforcommit: modified: ReadMe

这表示修改还停留在工作区,没有进入暂存区。

如果想丢弃工作区修改,可以使用:

gitcheckout -- ReadMe

执行后,ReadMe会恢复到最近一次git addgit commit时的状态。

这里有一个非常重要的点:

git checkout -- 文件名中的--不要省略。

因为省略后,checkout可能会被 Git 理解成切换分支等其他含义。

现在新版本 Git 更推荐使用:

gitrestore ReadMe

它的语义更直观:恢复工作区文件。

所以这两个命令可以对应理解:

gitcheckout -- ReadMegitrestore ReadMe

它们都可以用于丢弃工作区修改。

不过如果你是跟着传统 Git 命令学习,知道git checkout -- file仍然很有必要,因为很多老项目和资料里都会看到它。

2. 已经 add,但还没有 commit

如果修改了ReadMe,并且已经执行:

gitaddReadMe

此时再查看状态:

gitstatus

可能会看到:

Changes to be committed: modified: ReadMe

这说明修改已经进入暂存区。

如果想撤销暂存区,可以使用:

gitreset HEAD ReadMe

这个命令的作用是:

ReadMe从暂存区撤出来,但工作区里的修改仍然保留。

执行后可能会看到:

Unstaged changes after reset: M ReadMe

这时再执行:

gitstatus

会发现文件又变成:

Changes not stagedforcommit: modified: ReadMe

也就是说,修改从暂存区退回到了工作区。

如果连工作区修改也不想要了,再执行:

gitcheckout -- ReadMe

或者:

gitrestore ReadMe

完整流程可以理解为:

# 先撤销暂存区gitreset HEAD ReadMe# 再丢弃工作区修改gitcheckout -- ReadMe

新版本 Git 中,也可以使用:

gitrestore--stagedReadMegitrestore ReadMe

其中:

gitrestore--stagedReadMe

表示把文件从暂存区撤出来。

gitrestore ReadMe

表示丢弃工作区修改。

3. 已经 commit

如果修改已经提交了,比如:

gitaddReadMegitcommit-m"bad change"

此时想撤销这次提交,就属于版本回退问题。

如果这个提交还没有推送到远程,可以使用:

gitreset--hardHEAD^

或者更清晰一点:

gitreset--hardHEAD~1

它表示回到上一次提交。

但是这里要非常小心:

如果这次提交已经推送到远程仓库,并且别人可能已经基于它继续开发,就不要随便用reset --hard改写历史。

在团队协作中,如果要撤销已经推送的提交,通常更推荐使用:

gitrevert

git revert会生成一次新的提交,用新的提交来抵消旧提交,而不是直接改写提交历史。

git revert后面讲远程协作时再展开。

现在先记住:

  • 本地未推送的错误提交,可以考虑git reset
  • 已推送、已共享的提交,不要轻易reset --hard
  • 团队协作中撤销公开提交,更推荐git revert

六、删除文件:恢复误删与彻底删除

在 Git 中,删除文件本身也是一种修改

比如当前目录中有:

file1 file2 file3 file4 file5 ReadMe

如果执行:

rmfile5

这只是从工作区删除了file5

此时查看状态:

gitstatus

会看到类似:

Changes not stagedforcommit: deleted: file5

这说明 Git 发现file5被删除了,但这个删除还没有提交到版本库。

此时一般有两种情况:

  • 文件是误删的,想恢复;
  • 文件确实不要了,想从版本库中删除。

1. 文件误删:恢复回来

如果file5是误删,可以使用:

gitcheckout -- file5

或者新版本 Git:

gitrestore file5

这样 Git 会从版本库中把file5恢复到工作区。

恢复后可以查看:

ls

如果file5重新出现,就说明恢复成功。

这里的原理是:

版本库里还有file5的历史记录,所以可以恢复工作区中的误删文件。

2. 文件确实不要了:从版本库删除

如果file5确实不要了,只执行:

rmfile5

还不够。

因为这只是删除了工作区文件,还没有把“删除这个动作”提交到版本库。

更标准的方式是:

gitrmfile5

然后提交:

gitcommit-m"delete file5"

完整流程:

gitrmfile5gitcommit-m"delete file5"

提交成功后,可能会看到:

delete mode100644file5

这说明file5已经从版本库记录中被删除。

当然,这并不表示 Git 历史里完全找不到file5了。

因为 Git 的历史提交仍然保存着旧版本。如果以后回到文件还存在的那个提交,依然可以看到它。

3. rm 和 git rm 的区别

可以这样理解:

rm file :只删除工作区文件 git rm file :删除工作区文件,并把删除动作加入暂存区

如果已经手动rm file5了,也可以再执行:

gitaddfile5

或者:

gitrmfile5

把删除动作加入暂存区。

不过日常使用时,如果确定要删除 Git 管理的文件,直接使用:

gitrmfile5

会更清晰。


七、常用撤销命令和风险提醒

这一篇涉及的命令比较多,最后做一个集中整理。

1. 回退版本

回退到指定提交:

gitreset--hardcommit_id

回退到上一个版本:

gitreset--hardHEAD^

或者:

gitreset--hardHEAD~1

2. 找回历史操作

查看HEAD的移动记录:

gitreflog

找到目标提交后,可以回去:

gitreset--hardcommit_id

3. 丢弃工作区修改

传统写法:

gitcheckout --file

新写法:

gitrestorefile

4. 撤销暂存区修改

传统写法:

gitreset HEADfile

新写法:

gitrestore--stagedfile

5. 删除文件

确认删除:

gitrmfilegitcommit-m"delete file"

误删恢复:

gitcheckout --file

或者:

gitrestorefile

6. reset 三种模式对照

可以用下面这个表简单记忆:

命令版本库暂存区工作区常见用途
git reset --soft回退不变不变撤回提交,保留暂存
git reset --mixed回退回退不变取消暂存,保留修改
git reset --hard回退回退回退彻底回到指定版本

其中最需要小心的是:

gitreset--hard

因为它会影响工作区。

所以使用前一定要确认:

gitstatusgitdiff

如果工作区有重要修改,先提交,或者至少先备份,不要直接--hard


总结

这一篇主要整理了 Git 中的版本回退、撤销修改和删除文件。

核心内容包括:

  • git reset可以让仓库回到指定版本;
  • --soft--mixed--hard影响的区域不同;
  • HEAD表示当前版本;
  • HEAD^表示上一个版本;
  • HEAD~n更适合表示往前回退 n 个版本;
  • commit id可以精确定位某次提交;
  • git reflog可以查看本地HEAD移动记录;
  • 工作区修改可以用git checkout -- filegit restore file丢弃;
  • 暂存区修改可以用git reset HEAD filegit restore --staged file撤销;
  • 已提交但未推送的错误提交,可以考虑用git reset回退;
  • 已经推送到远程并被别人使用的提交,不要随便reset --hard
  • 删除文件后,如果是误删,可以恢复;
  • 如果确认删除,要使用git rm并提交删除动作。

这篇最重要的理解是:

撤销之前先判断文件处在哪个区域。

工作区、暂存区、版本库对应的撤销方式不同。

只要区域判断清楚,Git 的“反悔操作”就不会乱。

下一篇会进入 Git 的分支基础,重点整理:

  • 分支到底是什么;
  • HEAD和分支的关系;
  • 如何创建、切换、合并、删除分支;
  • 什么是 Fast-forward;
  • 为什么会产生合并冲突;
  • 如何手动解决冲突。
http://www.rkmt.cn/news/1394173.html

相关文章:

  • Fabrica:基于确定性状态机的AI编程工程化工作流实践
  • Python 开发者如何通过 OpenAI 兼容协议快速接入 Taotoken 调用大模型
  • 在内容生成业务中利用Taotoken灵活调用不同模型优化输出质量
  • 2026年电商侵权应诉与专利无效宣告服务商深度对比|义乌知识产权维权指南 - 年度推荐企业名录
  • 揭秘高效Excel数据处理:现代PHP开发者的智能解决方案
  • HASS.Agent:5个必知技巧让你在Windows上完美集成Home Assistant
  • 2026年金华专利申请与电商侵权应诉完全指南:从被动应诉到主动反制的终极防守手册 - 年度推荐企业名录
  • STM32H7实战避坑指南:从高性能外设到复杂应用场景
  • 3分钟搞定通达信缠论分析:ChanlunX开源插件终极指南
  • SFC高可用与绿色节能双目标优化:动态冗余与预测检查点实践
  • VSC交直流混合系统潮流计算:快速灵活全纯嵌入法原理与工程实践
  • 【ChatGPT文献综述生成实战指南】:20年科研老炮亲授5步法,3小时内产出Nature级综述初稿
  • RISC-V Packed-SIMD加速方案P-Box:为嵌入式边缘计算注入并行处理能力
  • ARMv8 A64 SIMD浮点转换指令FCVTAU与FCVTMS详解
  • 海康综合安防平台API对接避坑指南:从AK/SK获取到RTSP/RTMP流播放的完整流程
  • CGGC-Net:基于图卷积与对比学习的点云语义分割模型详解
  • 2026年杭州电商新趋势:专业公司如何引领未来市场
  • 从车间排班到路径规划:禁忌搜索算法(Tabu Search)在工业界的5个真实应用案例
  • 智能体为什么是 AI 终局?
  • QueryExcel:100个Excel文件秒级搜索,彻底告别繁琐查找的终极解决方案
  • 杰理之获取蓝牙名无效果【篇】
  • USB设备开发避坑:为什么你的高速设备在全速模式下会‘失联’?聊聊Device Qualifier Descriptor
  • ROS 调试方法
  • 【16位实模式MD模拟器】第二篇:解剖16位霸主(下) ── 世嘉官方 Memory Map 深度切片 仅自己可见
  • Steam Deck终极双系统引导管理工具:如何实现一键切换的完美解决方案
  • k8s之POD资源限制和健康监测
  • 免费跨平台B站视频下载器:BilibiliDown完整使用指南与技巧分享
  • Level国际化与本地化:Gettext多语言支持实现方案
  • 7.13 云上安装Docker容器环境
  • 7.11 云上搭建Python开发环境