6. Git stash 恢复进度

我们之前使用 git stash 命令,将当时的修改进行了保存,保存的内容为: 新增了 a/b/c/hello.txt 文件,并添加了一行 Bye Bye.welcome.txt 文件新增了一行 Bye Bye.

实际上,恢复进度也是用这个命令。我们先看一下当前的分支状态和日志信息:

image.png

现在,我们处于 master 分支上,,查看保存的进度命令如下:git stash list

image.png

可使用 git stash pop 从最近保存的进度进行恢复 :

image.png

可以看到,之前保存的内容全都恢复了,列表中的该保存记录被销毁了。仔细查看一下输出:hello.txt 文件已经 add 了 ,等待 commitwelcome.txt 文件修改了,还没被 addstash@{0} 这个进度记录被 drop(删除)了。

再查看一下工作区的状态:

image.png

发现跟恢复进度时的输出是一致的。

现在,我们根据之前学过的各种命令,进行各种操作:

  1. 把当前暂存区的内容进行提交,即只提交 a/b/c/hello.txt ,不提交 welcome.txt
image.png
image.png

可以看到hello.txt 文件被提交了,welcome.txt 文件并没有。

  1. 现在后悔了,想回到之前的状态,想要撤回这个提交:

可用命令 git reset --soft HEAD^ ,注意要加上 --soft ,只是将分支提交记录回退到上一次提交。如果用默认的 --mixed ,会覆盖掉暂存区的。

image.png

可以看到,之前提交的内容回来了,提交记录也是之前的。

  1. 如果想把 welcome.txt 进行提交,而把 hello.txt 撤出暂存区,可以这样操作:
image.png
image.png

这是 reset 的另一种用法,该用法不会改变引用,也不会改变工作区,而是用指定提交状态下(这里是 HEAD)的文件替换掉暂存区中的文件。会回到 git add 命令之前的状态。

  1. 突然又不想提交 welcome.txt 文件,想从暂存区撤出(git reset 等价于 git reset --mixed HEAD) :
image.png
  1. 现在,想把工作区所有修改全部清除掉,包括 welcome.txt 的改动和目录 a 以及下面的子目录和文件,可以使用 git checkout . 命令:
image.png

现在工作区还有一个多余的目录 a ,git clean -nd 命令可以查看哪些文件和目录可以被删除,以免造成误删除:

image.png

发现 a 目录是可以被正常删除的,这时候再强制删除多余的目录和文件:

image.png
image.png

此时,工作区变得非常干净了。

最后,我们再详细讲解一下 git stash 的用法:

git stash 用于保存和恢复工作进度,这个命令非常有用。

  • 命令: git stash
    作用:保存当前的工作进度,会分别对暂存区和工作区的状态进行保存。

  • 命令:git stash list
    作用:显示进度列表。很明显可以对工作进度进行多次保存操作,并且在恢复的时候可以进行选择。

  • 命令: git stash pop [ ]
    作用:如果不使用任何参数,默认恢复最新保存的工作进度,并将恢复的工作进度从存储列表中移除。如果加上 stash 参数(来自 git stash list 显示的列表),则从指定 stash 中恢复,恢复之后也将该进度从存储列表中移除。

  • 命令: git stash [ save ] [ -k ] []
    作用:通过 save 可以在保存进度的时候指定存储说明,格式如下:git stash save "this is a message",如果再加上 -k 参数,则保存进度之后不会将暂存区重置,默认会将暂存区和工作区强制重置。

  • 命令: git stash apply [ ]
    作用:跟 git stash pop [ ] 功能一样,但是恢复之后,该进度不会从存储列表中移除。

  • 命令: git stash drop []
    作用:删除存储记录,默认删除最新保存的记录,可指定记录进行删除。

  • 命令:git stash clear
    作用:清空所有的存储记录。

  • 命令:git stash branch
    作用:基于存储记录来创建分支。(分支功能后面再讲)

简单探秘一下 git stash 的机制,通过示例演示一下:

当前,我们没有存储任何进度,列表为空:

image.png

welcome.txt 文件中添加一行 Bye-Bye. ,并创建 hack-1.txt 文件(文件内添加一行 hello.):

image.png

可以看到,暂存区中添加了新创建的 hack-1.txt ,修改过的welcome.txt 并没添加到暂存区,现在我们将当前的进度保存:

image.png
image.png

结果是:工作区恢复到了修改前的状态(实际使用了 git reset --hard HEAD 命令),文件 welcome.txt 的修改不见了,新增的文件 hack-1.txt 也不见了。

接下来,我们再做一个修改,新创建 hack-2.txt 文件,并添加一行文本为 fix. ,并尝试保存进度:

image.png

很遗憾,保存失败,说没有任何变更内容需要保存。可见,本地没有被版本控制系统跟踪的文件并不能保存进度,只能先 add 再保存:

image.png

现在有两个保存记录了,如下:

image.png

在保存进度时,最好提供说明,这样可以更好地通过进度列表找到保存的进度。每个进度的标识都是 stash@{} 格式,像极了前面介绍的 reflog 的格式。实际上, git stash 命令就是用前面介绍的引用和引用变更日志 reflog 实现的:

image.png

可以看到,在 .git/refs/.git/logs/refs 目录下,都存在 stash 文件。

跟分支引用一样, refs/stash 保存的就是 statsh list 中最新的提交ID ,还能看到该提交的相关记录:

image.png

简单总结一下:git stash 保存进度,实际上会将进度保存在引用 refs/stash 所指向的提交中。多次的进度保存,会指向最新的保存提交ID,而 refs/stash 引用的变化由 logs/refs/stash 记录下来。

那么,引用(refs/stash) 是怎么同时保存暂存区进度和工作区中的进度呢?我们查看一下 refs/stash 的提交历史 【把 stash 看作分支对待】:

image.png

提交说明中的 WIP ,表示 Work In Progress 工作区进度,而 index on master ,包含 index 字眼,表示暂存区进度。而且最新的提交是一个合并提交。

下面,我们来研究一下第一次的进度保存:

image.png
image.png

上面显示的三个提交对应着三棵不同的树。我们先把不同的状态区分出来,用 ‘原基线’ 代表进度保存时版本库的状态,即提交ID 4448fe8705 ;用 '原暂存区' 代表进度保存时暂存区的状态,即提交ID 16ab29038 ;用 '原工作区' 代表进度保存时工作区的状态,即提交ID d0f05c922

现在,开始对比各种差异:

^ 用法讲解: ^2 表示中的 2 表示是第几个父提交,比如上面的 stash@{1} 表示最新的提交 ID , stash@{1}^2 ,表示其中的第二个父提交,也就是 16ab29038,那么 stash@{1}^2^ 就表示 16ab29038 的父提交,也就是 4448fe8705

  1. 原基线和原暂存区的差异比较:
image.png

刚好是第一次保存时,暂存区添加了新文件 hack-1.txt 并在里面添加了一行 hello. ;(不记得可以往前面的内容再看一下)

  1. 原暂存区和原工作区的差异比较:
image.png

刚好是第一次保存时,工作区的 welcome.txt 中添加了一行 Nice to meet you. ;(不记得可以往前面的内容再看一下)

  1. 原基线和原工作区的差异比较:
image.png

刚好是 1 和 2 的差异汇总 。

最后,我们用 stash@{1} 来恢复进度,再清空所有保存的进度:

image.png
image.png

清空之后,会发现 stash 相关的引用和 reflogstash 文件都不见了,对不找对应的文件了:

image.png

你可能感兴趣的:(6. Git stash 恢复进度)