【Git常用】之回滚

it2022-05-05  230

一、基础

回滚是一个版本管理系统最重要的功能之一。 由于Git的设计,导致Git的回滚在新手面前十分难理解。 本文从结果出发,总结了对于使用者来说最常用的操作,在不了解Git的深层原理时,可以快速上手使用。

二、说明

实验环境:Windows 10,Git版本2.21.0 为了方便演示,编者自己只做了一个简单的Git项目,项目中只有一个README.md文件。 第一次提交内容为v1,第二次为v2,第三次为v3,现在的提交历史及仓库状况如下图所示。 一图胜千言。 我们把仓库(Repository)、暂存区(Index)和工作目录(Working Directory) 同时画进图中,方便后续理解。 大概就是这个样子:我们在master分支上提交了三次,所以仓库中有三次记录,暂存区和最新的提交一样,工作目录我们没有修改什么,和仓库一样。

深入理解这张图片需要理解如下内容:

分支的本质是一系列提交的链接,分支(图中master)一般情况下指向最新的提交(链末尾),我们通过引用指向提交的指针来操作分支。HEAD也是一个指向提交的指针,表示当前所在的分支,一般情况下指向当前分支。Git存储文档的方式见【Git常用】之深入理解Git存储文档的方式。

三、常用操作总结

3.1 回顾add与commit

将工作目录中的README修改为v4,本次提交想输入的提交信息为“forth commit”,现在如下: 运行$ git add README.md,相信你一定可以理解下图。 运行$ git commit -m 'forth commit',就变成了如下的样子。

3.2 取消add

这里说“取消add”,可以理解为(git的做法)将Repository的最新提交覆盖到Index中。

按照git bash中的提示,使用命令$ git reset HEAD <file>可以达到此目的。

我们回到3.1中运行$ git commit,提交v4之前的这一步。 运行$ git reset HEAD README.md,运行结果如图 想要理解这个命令做了什么,请看3.4。

3.3 取消修改

很简单,就是我修改的都不想要了,是整个工作目录回退到一个干净的状态,和仓库的代码一致。

我们回到3.1中$ git add,添加v4之前这一步。 运行$ git checkout -- README.md,运行结果如图

3.4 取消一次提交

以上展示的都没有操作git提交的历史,都是在完好的提交历史基础上进行三个区域的操作。

假设v4版本的提交不想要了,又如何操作呢?

让我们回到这一步。 经历过3.2,我们思考,是不是把v3提交从Repository中复制出来,复制给Index和Working Directory不就搞定了?

Git确实也是这么做的。

使用$ git reset --hard HEAD^,执行结果:

解析:

第一步,将HEAD和master向目标提交移动,HEAD^表示HEAD所在的提交的父提交第二步,将目标提交复制到Index。第三部,将Index复制到Working Directory。-- hard参数在这里起了作用,表示需要修改Working Directory

有意思的是,我们可以通过指定参数,让git reset命令并不都执行这三步。

$ git reset --soft HEAD^只会执行第一步,这样的结果看起来如下。 $ git reset --mixed HEAD^只会执行到第二步,这样的结果看起来如下。 等等,这个命令的作用,和之前3.2提到的$ git reset HEAD <file>有异曲同工之妙。

是的,没错,他们是一个命令。原因在于:

$ git reset默认携带--mixed参数$ git reset HEAD <file>指定的提交是HEAD,即master最新的提交,所以在3.2的条件下,git将v3的内容复制出来给Index

这就是git的做法。

还有一个区别是3.2的命令指定了一个文件,这个很好理解,就是该文件单独处理。

总结一下,关于$ git reset

第一步,移动HEAD和分支指针到目标提交。git reset --soft [branch]在此停止。第二步,将目标提交复制到Index。git reset <--mixed> [branch]在此停止。第三部,将Index复制到Working Directory。git reset --hard [branch]在此停止。

想要修改某些文件,则可以使用git reset <--mixed> [file],--soft和--hard都不可以加文件

但是这会跳过第一步!

我们来尝试一下,仓库如下图所示状态。 执行$ git reset --mixed HEAD^ README.md,结果如下。 可以看到,HEAD和master没有移动,但是Index变成了v3的内容,即执行了第二步,将HEAD的父提交v3复制到Index,并停止。

3.5 checkout

现在来理解checkout。

$ git checkout [branch]和$ git reset --hard [branch]一样,都可以操作三个区域。不同的是:

checkout只移动HEAD,不改变master(举例来说)。checkout不会像--hard一样强制修改Working Directory,他会检查甚至自动合并。

运行$ git checkout 2b0d18b结果如图。 这时git处于“detached HEAD”状态。

如果使用$ git checkout -- <file>,则一样会跳过“第一步“和”第二步“,使用HEAD中文件覆盖工作区域。

reset命令并没有删除提交,v4还在那里,我们可以使用SHA-1值去索引到它。

四、总结

三个区域的文件迁移关系如下图所示。

注:

这里的迁移关系只是示意图,并没有表示出命令完整的作用。reset有三个步骤,请理解以后再来看此图。关于$ git checkout -- <file>的作用,编者亲自做了实验,发现并没有修改Index区,和《Git Pro》中的说法不一致,欢迎大家留言讨论。

最新回复(0)