Git原理与使用详解(四):时光回溯——版本回退与修改撤销

一、 引言:错误是常态,回退是刚需

在软件开发过程中,犯错是不可避免的。我们可能会写出有Bug的代码、提交了不完整的逻辑、甚至误删了重要文件。一个强大的版本控制系统,其价值不仅在于记录每一次正确的提交,更在于当错误发生时,能提供一套安全、可靠的"后悔药"机制,让我们能够从容地回溯到任何一个正确的历史版本。

Git正是这方面的专家。它提供了强大的工具来帮助我们进行版本回退和修改撤销,理解并善用这些功能,是每一位开发者必须掌握的生存技能。本篇将详细讲解如何使用Git进行版本回退、撤销工作区和暂存区的修改,以及安全地删除文件。

二、 版本回退:回到过去的任意时刻

想象一下,你刚刚提交了几个版本,但发现当前版本的代码引入了严重的问题,希望将代码库恢复到某个历史版本的状态。这时就需要用到 git reset命令。

2.1 准备工作:创建多个版本

为了演示回退功能,我们先模拟创建三个版本的 ReadMe文件。

  1. 版本1:首次提交

    复制代码
    liu@139-159-150-152:~/gitcode$ echo "hello world - version1" > ReadMe
    liu@139-159-150-152:~/gitcode$ cat ReadMe
    hello world - version1
    liu@139-159-150-152:~/gitcode$ git add ReadMe
    liu@139-159-150-152:~/gitcode$ git commit -m "add version1"
    [master cff9d1e] add version1
     1 file changed, 1 insertion(+), 2 deletions(-) # 注意这里会显示之前的行被删除,新行被插入
  2. 版本2:第二次提交

    复制代码
    liu@139-159-150-152:~/gitcode$ echo "hello world - version2" > ReadMe
    liu@139-159-150-152:~/gitcode$ cat ReadMe
    hello world - version2
    liu@139-159-150-152:~/gitcode$ git add ReadMe
    liu@139-159-150-152:~/gitcode$ git commit -m "add version2"
    [master 14c12c3] add version2
     1 file changed, 1 insertion(+), 1 deletion(-)
  3. 版本3:第三次提交

    复制代码
    liu@139-159-150-152:~/gitcode$ echo "hello world - version3" > ReadMe
    liu@139-159-150-152:~/gitcode$ cat ReadMe
    hello world - version3
    liu@139-159-150-152:~/gitcode$ git add ReadMe
    liu@139-159-150-152:~/gitcode$ git commit -m "add version3"
    [master d95c13f] add version3
     1 file changed, 1 insertion(+), 1 deletion(-)

现在,我们使用 git log --pretty=oneline查看简洁的提交历史:

复制代码
liu@139-159-150-152:~/gitcode$ git log --pretty=oneline
d95c13f8a12b4e2c1f8a5e3c7b8d9a0e1f2a3b4c5 (HEAD -> master) add version3
14c12c32464d6ead7159f5c24e786ce450c899dd add version2
cff9d1e8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2 add version1

注:这里的commit id是示例,你实际操作时会是不同的哈希值。

2.2 理解HEAD指针

在回退之前,必须理解 HEAD ​ 指针。HEAD可以理解为 Git 中的"当前激活状态"或"你正在看的地方"的标签。在大多数情况下,HEAD指向某个分支(比如 master),而分支指针则指向某次具体的提交。

  • 初始状态:HEAD-> master-> version3提交

  • 当我们回退时,实际上是在移动 HEAD(以及它所指向的分支)的指向。

2.3 git reset命令详解

git reset命令的语法格式为:git reset [--soft | --mixed | --hard] [HEAD]

这个命令的本质是移动 HEAD指针(以及当前分支)到指定的提交 。而 --soft--mixed--hard这三个参数则决定了 如何处理工作区和暂存区的内容

  • --soft最温和 的回退。仅版本库 回退到指定版本,工作区和暂存区的内容保持不变 。这意味着你之前的修改仍然处于 git add后的暂存状态。适用于你想重新提交一次(修改提交信息或合并几次提交)。

  • --mixed默认选项 ):混合模式版本库暂存区 都回退到指定版本,但工作区文件保持不变 。这意味着你之前的修改仍然存在,但变成了"未暂存"的状态(Changes not staged for commit)。这是最常用的模式,相当于撤销了 git add操作。

  • --hard最彻底 的回退。版本库、暂存区和工作区 全部回退到指定版本。**这个命令非常危险!**​ 因为它会丢弃自指定版本以来你所有的修改(包括工作区和暂存区的)。使用此命令前,请务必确认工作区没有未提交的重要代码。

HEAD说明:

  • 可以直接写成 commit id,如 d95c13f,表示指定退回的版本。

  • HEAD表示当前版本。

  • HEAD^表示上一个版本。

  • HEAD^^表示上上一个版本。

  • 以此类推...

  • 可以使用 ~数字表示:

    • HEAD~0表示当前版本。

    • HEAD~1上一个版本。

    • HEAD~2上上一个版本。

2.4 实战:回退到version2

假设我们在提交完 version3后,发现 version3有严重错误,想回退到稳定可靠的 version2。因为我们希望工作区的代码也回到 version2的状态,所以使用 --hard参数。

复制代码
# 当前在 version3,我们想回退到上一个版本 version2
liu@139-159-150-152:~/gitcode$ git reset --hard HEAD^
HEAD is now at 14c12c3 add version2

# 查看ReadMe文件内容,确认已回退
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version2

# 查看日志,HEAD指向了version2
liu@139-159-150-152:~/gitcode$ git log --pretty=oneline
14c12c32464d6ead7159f5c24e786ce450c899dd (HEAD -> master) add version2
cff9d1e8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2 add version1

成功!代码已经回到了 version2的状态。

2.5 再次回退与后悔药 git reflog

现在,我们后悔了,觉得还是 version3更好,想再回去。但问题来了:git log已经看不到 version3的提交记录了!它的 commit id (d95c13f) 我们也忘了。

这时,Git 的终极后悔药------git reflog登场了。git reflog记录了你的本地仓库的所有操作历史(包括提交、重置、合并等),即使这些提交已经不在当前分支的历史线中。

复制代码
liu@139-159-150-152:~/gitcode$ git reflog
14c12c3 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
d95c13f HEAD@{1}: commit: add version3
14c12c3 (HEAD -> master) HEAD@{2}: commit: add version2
cff9d1e HEAD@{3}: commit: add version1

reflog中,我们清晰地看到了所有操作:

  1. HEAD@{3}:提交了 version1

  2. HEAD@{2}:提交了 version2

  3. HEAD@{1}:提交了 version3(commit id: d95c13f)。

  4. HEAD@{0}:我们从 version3回退到了 version2

现在,我们可以使用 d95c13f这个 commit id 回到 version3

复制代码
liu@139-159-150-152:~/gitcode$ git reset --hard d95c13f
HEAD is now at d95c13f add version3

liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3

完美!我们成功利用 git reflog找回了"丢失"的提交。

重要提示git reflog记录的是本地操作,它有一定有效期(默认90天),并且不会被推送到远程仓库。它是你本地开发的强力安全保障。

三、 撤销修改:丢弃不需要的更改

版本回退是针对已提交的版本。而对于工作区暂存区中尚未提交的修改,我们需要使用撤销命令。

情况一:撤销工作区的修改(还未 git add

假设你正在修改 ReadMe,写了一半发现写得不对,想恢复到修改前的状态(即最近一次 git commitgit add时的状态)。

  1. 制造一个"错误"的修改

    复制代码
    liu@139-159-150-152:~/gitcode$ echo "This is a terrible mistake!" >> ReadMe
    liu@139-159-150-152:~/gitcode$ cat ReadMe
    hello world - version3
    This is a terrible mistake!
  2. 查看状态,发现修改未被暂存

    复制代码
    liu@139-159-150-152:~/gitcode$ git status
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory) # Git的提示
            modified:   ReadMe

    Git 友好地提示我们可以使用 git restore <file>来丢弃工作区的修改。这是一个较新的命令。传统的命令是 git checkout -- <file>

  3. 撤销工作区修改

    复制代码
    # 使用传统命令(仍然有效)
    liu@139-159-150-152:~/gitcode$ git checkout -- ReadMe
    
    # 或者使用新命令(推荐)
    # liu@139-159-150-152:~/gitcode$ git restore ReadMe
    
    # 检查文件,修改已被撤销
    liu@139-159-150-152:~/gitcode$ cat ReadMe
    hello world - version3

    命令解释git checkout -- <file>git restore <file>命令会用暂存区 的版本覆盖工作区的版本。如果暂存区没有该文件的修改记录(即你没执行过 git add),则会用版本库中最新提交 的版本来覆盖。这个操作不可逆,请谨慎使用!

情况二:撤销暂存区的修改(已经 git add,但未 git commit

如果你不小心把不该提交的文件 git add到了暂存区,可以将其从暂存区撤回到工作区。

  1. 再次制造修改并添加到暂存区

    复制代码
    liu@139-159-150-152:~/gitcode$ echo "This change is not ready yet." >> ReadMe
    liu@139-159-150-152:~/gitcode$ git add ReadMe
    liu@139-159-150-152:~/gitcode$ git status
    On branch master
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage) # Git的提示
            modified:   ReadMe
  2. 使用 git reset(默认 --mixed)将修改撤出暂存区

    复制代码
    # 将ReadMe文件从暂存区撤出,放回工作区
    liu@139-159-150-152:~/gitcode$ git reset HEAD ReadMe
    # 或者使用更语义化的新命令
    # liu@139-159-150-152:~/gitcode$ git restore --staged ReadMe
    
    liu@139-159-150-152:~/gitcode$ git status
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
            modified:   ReadMe

    现在,修改又回到了"未暂存"的状态。此时,如果你想彻底丢弃这个修改,可以再使用情况一中的命令。

情况三:撤销已提交的修改(已经 git commit

这其实就是我们前面讲的版本回退 。可以使用 git reset --hard HEAD^回退到上一个版本。但切记,一旦提交推送到了远程仓库,再使用 git reset --hard回退并强制推送 (git push -f) 会重写历史,对团队协作影响很大,需极其谨慎。

四、 删除文件

在Git中,删除文件也是一个需要被版本控制的"修改"操作。

场景:从版本库中删除文件

假设我们有一个文件 useless_file.txt需要从版本库中删除。

  1. 直接在工作区删除文件是无效的

    复制代码
    liu@139-159-150-152:~/gitcode$ rm useless_file.txt
    liu@139-159-150-152:~/gitcode$ git status
    On branch master
    Changes not staged for commit:
      (use "git add/rm <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
            deleted:    useless_file.txt # Git检测到文件被删除,但未暂存

    这只会删除工作区的文件,版本库中仍然存在。

  2. 正确做法:使用 git rm命令

    git rm命令会同时完成工作区删除暂存区删除

    复制代码
    # 先恢复文件,演示正确流程
    liu@139-159-150-152:~/gitcode$ git checkout -- useless_file.txt
    
    # 使用 git rm 删除
    liu@139-159-150-152:~/gitcode$ git rm useless_file.txt
    rm 'useless_file.txt'
    
    liu@139-159-150-152:~/gitcode$ git status
    On branch master
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
            deleted:    useless_file.txt # 删除操作已暂存
  3. 提交删除操作

    复制代码
    liu@139-159-150-152:~/gitcode$ git commit -m "remove useless_file.txt"
    [master xxxxxxx] remove useless_file.txt
     1 file changed, 0 insertions(+), 0 deletions(-)
     delete mode 100644 useless_file.txt

    至此,文件才真正从版本库中被删除。

误删恢复

如果不小心误删了文件,怎么办?很简单!因为版本库里还有这个文件,你只需要用撤销工作区修改的命令即可恢复:

复制代码
# 假设误删了 ReadMe
liu@139-159-150-152:~/gitcode$ rm ReadMe
# 恢复文件
liu@139-159-150-152:~/gitcode$ git checkout -- ReadMe
# 文件回来了!
liu@139-159-150-152:~/gitcode$ ls
ReadMe

五、 总结

本篇我们深入学习了Git的"后悔药"体系:

  1. 版本回退 (git reset):用于处理已提交的版本。

    • --soft:动版本库。

    • --mixed(默认):动版本库和暂存区。

    • --hard(危险):动版本库、暂存区和工作区。慎用!

  2. 撤销修改

    • 工作区修改未暂存:git restore <file>git checkout -- <file>

    • 修改已暂存未提交:git restore --staged <file>git reset HEAD <file>

    • 修改已提交:使用版本回退。

  3. 删除文件 :必须使用 git rmgit commit,才能从版本库中删除。

  4. 终极后悔药git reflog,用于找回丢失的提交或误操作的历史。

掌握这些命令,你就能在代码的时空里安全地自由穿梭。在下一篇中,我们将进入Git最强大的功能之一:分支管理。它将彻底改变你的开发协作方式。

相关推荐
春日见2 小时前
Git 相关操作大全
linux·人工智能·驱动开发·git·算法·机器学习
【赫兹威客】浩哥3 小时前
【赫兹威客】完全分布式Spark测试教程
大数据·分布式·spark
像豆芽一样优秀3 小时前
深入理解与应用SQL递归CTE处理层级数据
大数据·hive·sql
無森~3 小时前
HBase搭建
大数据·数据库·hbase
x新观点3 小时前
2026年亚马逊广告AI工具推荐:AI驱动优化成卖家新宠
大数据·人工智能
@zulnger3 小时前
git的基本操作
git
说私域3 小时前
共生与赋能:产品与运营的一体化逻辑——以AI智能名片链动2+1模式S2B2C商城系统为例
大数据·人工智能·产品运营·流量运营·私域运营
__xu_3 小时前
【总结】查看某个文件git提交记录的两种方法
git·vscode·提交记录
驭白.3 小时前
当硬件成为载体:制造端如何支撑持续的OTA与功能进化?
大数据·人工智能·ai·制造·数字化转型·制造业·新能源汽车