Git实用教程6:回到过去(回滚相关命令)

时间:2024-03-17 08:00:21

本节主要知识点:


Git实用教程6:回到过去(回滚相关命令)
回滚相关命令

  • git add 命令用于把工作目录的文件放入暂存区域
  • git commit 命令用于把暂存区域的文件提交到 Git 仓库
  • git reset 命令用于把 Git 仓库的文件还原到暂存区域
  • git checkout 命令用于把暂存区域的文件还原到工作目录

reset 回滚快照三步曲

  1. 移动 HEAD 的指向(–soft)
  2. 将快照回滚到暂存区域([–mixed],默认)
  3. 将暂存区域还原到工作目录(–hard)

回滚指定快照

在命令行中将路径切换至文件夹中,然后使用命令git reset + 具体的快照 ID(一般5位就行)'

回滚个别文件

在命令行中将路径切换至文件夹中,然后使用命令git reset 快照 文件名/路径 '

reset 不仅是一个“复古”的命令,它不仅可以回到过去,还可以去到“未来”。不仅可以往回滚,还可以往前滚!

失去了以前版本的ID

在命令行中将路径切换至文件夹中,然后使用命令git reflog'




上一讲无意中接触到了两个有关回退的命令:resetcheckout
Git实用教程6:回到过去(回滚相关命令)
现在几个命令应该相当清晰了:

  • git add 命令用于把工作目录的文件放入暂存区域
  • git commit 命令用于把暂存区域的文件提交到 Git 仓库
  • git reset 命令用于把 Git 仓库的文件还原到暂存区域
  • git checkout 命令用于把暂存区域的文件还原到工作目录

前边两个命令我有信心你已经相当熟悉了,但后边两个千万别说你已懂,因为它们是 Git 里边最复杂的命令(它们的功能可不止上边文字描述的这么简单

先给大家重点讲解 reset 命令,checkout 命令在分支管理中再细讲。

先执行 git log 命令查看历史提交:

Git实用教程6:回到过去(回滚相关命令)
这里记录了我们之前的 3 次提交(排序是按时间从近到远的),Author 后边是提交者,Date 后边是提交日期,下边是当次提交的说明。那……草黄色的那个 commit +“乱码”是 Git 为每次提交计算出来的 ID,它其实一个完整的 SHA-1 校验和(尽管你的文件内容可能跟我的完全一致,但这个值却不一样,这是因为账号、时间不同而导致),在任何时候都是唯一的,通过这个 ID,你就可以找到对应的那个版本。

根据 log 记录,现在我们将 Git 仓库如果可视化,应该是这样子:
Git实用教程6:回到过去(回滚相关命令)

回滚快照


注:快照即提交的版本,每个版本我们称之为一个快照。

现在我们利用 reset 命令回滚快照,并看看 Git 仓库和三棵树分别发生了什么。

执行 it reset HEAD~ 命令:

注:HEAD 表示最新提交的快照(31f46),而 HEAD~ 表示 HEAD 的上一个快照(d19e3)
Git实用教程6:回到过去(回滚相关命令)
然后执行 git status 命令查看现在的状态:

Git实用教程6:回到过去(回滚相关命令)
现在我们的快照(d19e3)回滚到 第二棵树(暂存区域)

有些朋友可能会持不同意见:不应该是回滚到第一棵树(工作目录)吗?你看,Git 不是写得很清楚吗 -> Changes not staged for commit,它还好心提醒我们使用 add 命令将修改添加到暂存区域丫!

其实真相是这样的:我们执行 git reset HEAD~ 命令之后,快照(d19e3)回滚到暂存区域,此时工作目录里存放的却是最新的文件(31f46)。由于 Git 会跟踪文件的变化,所以执行 git status 命令时,git 发现工作目录中的文件比暂存区域的要新(对比日期),所以才有这样的提示……

好了,现在执行完 git reset HEAD~ 命令之后,Git 仓库应该是这样子:
Git实用教程6:回到过去(回滚相关命令)
三棵树现在应该是下面酱紫:
Git实用教程6:回到过去(回滚相关命令)
这里有一点要补充的:HEAD~ 表示 HEAD 的上一个快照(d19e3),HEAD~~(00c29)则表示 HEAD 的上上一个快照,如果希望表示上 上 上 上 上 上 上 上 上 上一个快照(数了一下,这里有 10 个“上” ),那么可以直接用 HEAD~10 来表示。

git reset HEAD~ 命令其实是 git reset --mixed HEAD~ 的缩写,因为 --mixed 选项是默认的,所以我们可以偷懒。

我们发现,git reset HEAD~ 命令其实影响了两棵树:首先是移动 HEAD 的指向,将其指向上一个快照(HEAD~);然后再将该位置的快照回滚到暂存区域。

为了灵活地操纵这三棵树,Git 还为 reset 命令安排了 --soft 和 --hard 选项

–soft 选项


加上 --soft 选项的结果是使得 reset 变“软”了,也就没有原来那么持久……

So, git reset --soft HEAD~ 命令就相当于只移动 HEAD 的指向,但并不会将快照回滚到暂存区域。

这个选项有什么作用呢?

事实它就是相当于撤消了上一次的提交(commit)。

一不小心提交了,后悔了,那么你就执行 git reset --soft HEAD~ 命令即可(此时执行 git log 命令,也不会再看到已经撤消了的那个提交)。

–hard 选项


加上 --hard 选项的结果是使得 reset 变“硬”……

你猜的不错,加上 --hard 选项,reset 不仅移动 HEAD 的指向,将快照回滚动到暂存区域,它还将暂存区域的文件还原到工作目录。

来,上点图吧!

刚才执行完 git reset HEAD~ 命令后,Git 仓库里的数据是这样:
Git实用教程6:回到过去(回滚相关命令)
三棵树是这样:
Git实用教程6:回到过去(回滚相关命令)
那么在这种状态下,我再执行 git reset --hard HEAD~ 命令:
Git实用教程6:回到过去(回滚相关命令)
Git 仓库中就剩下最后一个快照了:
Git实用教程6:回到过去(回滚相关命令)
还原案发现场,Git 仓库现在应该是这样:
Git实用教程6:回到过去(回滚相关命令)
而三棵树现在应该都被回归到第一个版本(00c2929):
Git实用教程6:回到过去(回滚相关命令)
不信?自己瞧瞧你的文件夹:
Git实用教程6:回到过去(回滚相关命令)
最后总结一下:reset 回滚快照三部曲

  1. 移动 HEAD 的指向(–soft)

  2. 将快照回滚到暂存区域([–mixed],默认)

  3. 将暂存区域还原到工作目录(–hard)

回滚指定快照

如果快照比较多,你又懒得去数有多少个“上”,那么你可以通过指定具体的快照 ID 来回滚该快照。

比如 git reset 00c2929
如上,你不必把辣么长的 ID 号都给输入进去,一般只要输入前几位(5 位或以上吧)就可以了。

回滚个别文件

reset 不仅可以回滚指定快照,还可以回滚个别文件。

命令格式为 git reset 快照 文件名/路径

这样,它就会将忽略移动 HEAD 的指向这一步(因为你只是回滚快照的部分内容,并不是整个快照,所以 HEAD 的指向不应该发生改变),直接将指定快照的指定文件回滚到暂存区域。

不仅可以往回滚,还可以往前滚!

这里需要强调的是:reset 不仅是一个“复古”的命令,它不仅可以回到过去,还可以去到“未来”。

唯一的一个前提条件是:你需要知道指定快照的 ID 号。

现在执行 git log 命令只剩下一个最原始的提交了:

Git实用教程6:回到过去(回滚相关命令)
但是将命令行窗口向上拉,我们可以喵到之前提交的几个版本 ID 号
Git实用教程6:回到过去(回滚相关命令)
所以我们可以执行 git reset --hard 31f46be 命令:
Git实用教程6:回到过去(回滚相关命令)
再次执行 git log 命令:
Git实用教程6:回到过去(回滚相关命令)
我们又回到了最新的版本!

是的,我们就这样在历史的长河里滚来滚去……

但是……故事还没完,如果某天你 reset --hard 将工作目录回滚到了某个版本,但特么的隔天你就后悔了(有封写给小花的情书也放里边)!此时命令行窗口早已关闭,你又没用小本本把每次 commit 的 ID 号给记下来,这可肿么办才好?

reflog 命令可以拯救你!

执行 git reflog 命令,告诉我,你看到了什么:
Git实用教程6:回到过去(回滚相关命令)
Git 偷偷记录下了你每一次的操作(无耻小人),第一列就是每次执行完命令,HEAD 指向的版本 ID 号啦~