LV01-Git-04-Git本地仓库-04-撤销与版本回退

本文主要是Git中撤销与数据恢复的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
点击查看本文参考资料
点击查看相关文件下载
--- ---

【说明】本节笔记的相关操作在Windows下进行,因为VS Code有个Git的插件,可以很直观的演示一些东西。由于Git安装后自带一个Git-Bash终端,所以就不用Win下的命令行啦,就用的这个终端,因为它里边的命令与Linux很类似,而windows中的命令行有些命令与linux并不相同,为了统一,还是用用Git自带的终端啦。另外VS Code是可以选择使用的终端的,我直接将VS Code使用的终端改成了git-bash,这样更方便一些。

一、文件准备

我们这里重新准备一个版本库用于这一节笔记的使用:

1
2
3
4
git init
touch readme.md
git add .
git commit -m "feat:创建本地版本库并进行第一次提交(readme文件为空)"
image-20230622182414553

二、撤销操作

1. 修改上次提交

1.1 情况说明

我们上边创建了一个空的readme.md文件,并且提交了,但是,现在我们希望创建一个test.c文件,并且将test.c这个文件的功能写入到readme文件中。

一般来讲,我们可以重新提交一次,但是这就意味着版本库中有两个提交记录,但是其实根本没必要有两个提交记录啊,readme文件本来就是记录一些说明信息的,那么现在我们既想将新文件提交上去,又不想产生两次提交记录,那么要怎么样解决呢?

1.2 命令说明

此时,为了解决上边的问题,我们可以运行带有 –amend 选项的提交命令来重新提交,这样可以避免让仓库历史纪录变得复杂,一般格式如下:

1
2
git commit --amend -m "新的描述"  # 如果不加-m参数,系统会让进行输入的
git commit --amend --no-edit # 不修改描述,只是添加新文件

这个命令会将暂存区中的文件提交。 如果自上次提交以来我们还未做任何修改(例如,在上次提交后马上执行了此命令), 那么快照会保持不变,而我们所修改的只是提交信息。

当我们在修补最后的提交时,与其说是修复旧提交,倒不如说是完全用一个 新的提交 替换旧的提交。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。

修补提交最明显的价值是可以稍微改进我们最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱我们的仓库历史。

1.3 使用实例

1.3.1 查看文件状态

我们需要创建一个test.c文件,然后再修改readme文件,完成后,文件状态如下:

image-20230622201302462

1.3.2 重新提交

我们重新提交这一次的修改:

1
2
git add .
git commit --amend # 重新提交

然后,Git就会打开默认的编辑器,可以看到之前的提交信息,我们编辑后保存会覆盖原来的提交信息。

image-20230622201515487

提交后如下图所示:

image-20230622201634100

1.3.3 提交记录

我们使用自定义的git命令查看一下提交记录:

image-20230622201731087

会发现,整个仓库就只有这一条提交记录。也就是我们第二次提交的结果代替了第一次提交,就相当于第一次提交没有存在一样。

2. 撤销对文件的修改

这部分说的文件修改还暂未提交到暂存库,也就是修改文件之后,git add之前的状态。

2.1 情况说明

我们现在对test.c做一系列的修改,这个时候我们并未但是改完之后,这些修改我不想要了,我并不想保留对这个文件的修改,这个时候怎么办,一行一行吧修改的地方删掉吗?是个办法,对于我们测试文件来说,比较简单,倒无所谓,可是当我们在大型项目中开发,修改了大量的文件的时候,也不想保留修改这该怎么办?也一个一个改嘛,这显然是不现实的。

那么我们该如何方便地撤消修改——将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)? 幸运的是,git status 也告诉了我们应该如何做。

2.2 git status的提示

我们来修改一下test.c文件,然后看一下git status命令有什么提示:

image-20230622202532011

可以看到,有这么一句话:

1
(use "git restore <file>..." to discard changes in working directory)

这个命令其实就可以用于撤销我们对于文件的修改。

2.3 撤销命令

其实可以达到撤销未暂存的文件的修改的命令不仅仅上边这一个,还有一个checkout,具体为什么git status命令没有给出提示,还不清楚,不过不重要,我们知道就可以了:

1
2
git checkout -- <file>...
git restore <file>...

<file>…可以是多个文件,当然要是我们向直接撤销所有文件的修改,那么可以直接用下边的两个命令:

1
2
git checkout -- .
git restore .

需要特别注意,这两个命令是很危险的命令。 我们对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它。 除非我们确实清楚不想要对那个文件的本地修改了,否则最好不要使用这个命令。

2.4 使用实例

我们用git checkout来试一下吧:

1
git checkout -- test.c

然后我们会看到如下信息:

image-20230622203353223

如果我们仍然想保留对那个文件做出的修改,但是现在仍然需要撤消,我们将会在 Git 分支 介绍保存进度与分支,这通常是更好的做法。在 Git 中任何 已提交 的东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用 --amend 选项覆盖的提交也可以恢复 (阅读 数据恢复 了解数据恢复)。 然而,任何我们未提交的东西丢失后很可能再也找不到了。

3. 取消暂存的文件

这一部分是针对我们修改了文件,并且已经通过git add添加到暂存区的文件。

3.1 情况说明

现在有这样的情况,我们已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add *或者git add . 暂存了它们两个。但是这两个文件我想作为两次提交,有两个提交记录便于追溯,那么如何只取消暂存两个中的一个呢?

3.2 git status的提示

我们来修改一下test.c和readme文件,然后看一下git status命令有什么提示:

image-20230622204320520

修改完毕后,我们将修改提交到暂存区,然后查看一下文件状态:

1
2
git add .
git status
image-20230622204454677

可以看到,有这么一句话:

1
(use "git restore --staged <file>..." to unstage)

这个命令其实就可以用于撤销我们对于文件的修改。

3.3 撤销命令

其实撤销提交到暂存区的修改为命令一共有两个,但是git提示只给出了一个:

1
2
git restore --staged <file>...
git reset HEAD <file>...

<file>…可以是多个文件,当然要是我们向直接撤销所有文件的修改,那么可以直接用下边的两个命令:

1
2
git restore --staged .
git reset HEAD .

这两个命令没有想象中那么危险,我们取消暂存区的修改后,这些修改还保留在工作区,只是回到了我们刚修改完文件的状态。

3.4 使用实例

我们用git restore来试一下吧:

1
git restore --staged test.c

然后我们会看到如下信息:

image-20230622205233019

可以看到test.c文件已经回到了未提交到暂存库的状态了。

3.5 提交更新

我们将上面的更改提交到版本库中,方便后边继续使用此仓库做测试。

1
2
3
git commit -m "feat:取消暂存文件测试(修改readme文件)"
git add .
git commit -m "feat:取消暂存文件测试(修改test.c文件)"

可以看到当前的提交记录如下:

image-20230622211911237

三、版本回退

1. 情况说明

现在我们产生了v1、v2、v3三次提交,如下图所示,主要以test.c修改来做测试:

image-20230622212625172

v1、v2和v3的提交,分别对应一行内容:

image-20230622212732221

现在,我们不想要v2和v3的提交了,我现在需要回到版本v1,这该怎么办?

2. 跳到指定版本

Git给我们提供了一个命令,用于跳到指定的提交节点去,也可以叫版本回滚吧:

1
git reset --hard <版本号> # 回到指定版本,版本号即为每次提交的hash值

3. 查看提交记录hash值

要想通过上边的命令来跳转到指定的版本,我们就需要知道这个指定的版本提交记录的hash值,我们有两个命令:

1
2
git reflog               # 查看所有提交记录(包括因为回滚丢失的记录)
git log # 查看所有提交记录

其实这里查看所有提交记录的时候,也可以使用 git log指令来查看,那么该指令与 git reflog 区别在哪里呢?

  • git reflog 命令可以查看所有分支的所有操作记录信息,包括已经被删除的 commit 记录和 reset 的操作。
  • git log 命令可以显示当前分支所有提交过的版本信息,不包括已经被删除的 commit 记录和 reset 的操作。(注意: 只是当前分支操作的信息)。

所以,当我们回滚版本回滚错了的时候,想要恢复回滚之前的版本的话, git reflog 就为该操作的实现提供了可能,后边会提到的。

4. 使用实例

4.1 查看提交记录

这一步主要是为了获取跳转的版本的hash值:

1
git mylog # 用自定义的方便看
image-20230622212625172

我们要想跳转到v1版本的话,它对应的hash值为b065bb7

4.2 跳转指定版本

我们现在执行下边的命令,跳转到v1提交的时候:

1
git reset --hard b065bb7

执行完毕后,我们会得到以下提示:

image-20230622213658491

然后我们再看一下提交记录和文件:

image-20230622213752920

可以看到文件回到了v1版本的时候,并且提交记录也只剩下v1及以前了。

5. git reflog的应用

5.1 情况说明

上边我们已经跳转到v1的版本了,可是这个时候,突然发现v2版本也是很重要的,我现在想到v2版本去,可是那次提交貌似被我们丢掉了哎?怎么办?这个时候git reflog就派上用场了。

5.2 查看所有提交记录

我们执行下边的命令,看一下会有什么输出:

1
git reflog

然后我们会得到以下输出信息:

image-20230622214138386

这两个不就是我们的v2和v3的提交嘛,这样我就就可以获取这两次提交的hash值了,例如v2提交记录的hash为68b6e33

5.3 跳转到指定版本

我们执行以下命令跳转到v2版本的提交:

1
git reset --hard 68b6e33

然后我们看一下文件和提交记录:

image-20230622214405045

可以看到目前已经位于v2提交记录的版本啦。