Git实用教程 4.0:回到过去

Git 是一部设计精良的时光机器,使用 Git ,我们可以在不同时期的版本之间来回穿梭,上节课我们无意间讲到了两个关于 回退版本的命令:reset 和 checkout

Git实用教程 4.0:回到过去_第1张图片

•add

–用于把工作目录的文件放入暂存区域

•commit

–用于把暂存区域的文件提交到Git仓库

•reset

–用于把Git仓库的文件还原到暂存区域

•checkout

–用于把暂存区域的文件还原到工作目录

add 和 commit 命令相信大家已经很熟悉了,但是 reset 和 checkout 命令你也不要说自己已经懂了,因为 这两个命令是 Git 里面最复杂的命令,它们的功能可不止我们刚才讲的这么简单。它们有很多复杂的功能也很实用,这节课我们就主要来讲解 reset 命令,checkout 命令我们会在分支管理的章节进行讲解。

我们在上一节课工作目录的基础上,执行 git log 命令:

E:\MyProject>git log
commit 08f7a42b90ff076d7f26fb6dbdbc9b86808256c0 (HEAD -> master)
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:28:24 2019 +0800

    change the LICENSE file

commit 7540e6c3dca0b17139054df7548fe0570fd741f2
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:27:03 2019 +0800

    add a LICENSE file

commit 1fe46d092b4a7f1c99ddac65d7c2958000325f1e
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:24:14 2019 +0800

    add a README file

E:\MyProject>

目前的 Git 仓库有3个版本,第一个是 add a README file;第二个是 add a LICENSE file;第三个是 change the LICENSE file。

为了方便大家理解,我们将其可视化,Git 仓库目前的情况应该就是这样子:

 Git实用教程 4.0:回到过去_第2张图片

HEAD 指向的是最新的版本,第一次提交我们只是增加了一个 README.md,第二次提交我们增加了 LICENSE,第三次提交我们修改了 LICENSE。v1 是第一版,v2 是第二版,我们也用不同的颜色加以区分。

三棵树应该就如下图所示:

Git实用教程 4.0:回到过去_第3张图片

因为我们最新的在工作目录修改后的 add 到了缓存区,然后commit 到了 Git 仓库,所以,现在 三棵树应该是一样的。

现在我们就来尝试使用 reset 命令 回滚快照。(快照就是指的一个版本的快照)

我们执行 git reset HEAD~ 

我们知道 HEAD 现在是指向 08f7a42.... 这个版本,而加一个 波浪线(~)表示它的上一个,也就是 7540e6c... 这个版本。

我们回车:

 

E:\MyProject>git reset HEAD~
Unstaged changes after reset:
M       LICENSE

E:\MyProject>

那谁知道现在我们的 7540e6c... 这个快照现在回滚到哪一棵树里了呢?

大家考虑一下。答案是第二棵树,就是暂存区。也就是说,我们执行了  git reset HEAD~  这个语句之后,前一个版本是回滚到了第二棵树里面。我们证明一下:

我们现在执行 git status:

E:\MyProject>git status
On branch master
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   LICENSE

no changes added to commit (use "git add" and/or "git commit -a")

E:\MyProject>

有些同学就会有不同意见了,看提示的话,这样子不是回滚到第一棵树吗,Changes not staged for commit: 就是说第一棵树的内容发生了改变,你还没暂存,那应该就是回滚到第一棵树才是没有暂存啊?

其实不是这样的,你的思维要反过来,因为 git 会时刻跟踪你的文件的版本变化,所以我们刚才把  7540e6c... 这个快照回滚到第二棵树这里,

目前的情况就是下图所示:回滚到了 7540e6c....版本,然后是在第二颗树暂存区这里。

Git实用教程 4.0:回到过去_第4张图片

这个时候,git status 就会检测到 文件不同了。然后就会提醒还没有 staged,还没有被暂存。

我们现在执行 git log:

E:\MyProject>git log
commit 7540e6c3dca0b17139054df7548fe0570fd741f2 (HEAD -> master)
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:27:03 2019 +0800

    add a LICENSE file

commit 1fe46d092b4a7f1c99ddac65d7c2958000325f1e
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:24:14 2019 +0800

    add a README file

E:\MyProject>

就会发现只有两个快照了,且明确显示现在 HEAD 是指向  7540e6c3dca0b17139054df7548fe0570fd741f2。

08f7a42.....这个快照并没有丢失,只是 HEAD 没有指向它,所以通过 git log 看不到它了,只能看到 HEAD 及其往前的,因为没有箭头指向后边。

ok。这里还有一点需要补充的:

HEAD~ 表示 HEAD 的上一个快照(7540e6...),HEAD~~(1fe46d...)则表示 HEAD 的上上一个快照,如果希望表示上上上上上上上上上上一个快照(数了一下,这里有 10 个“上” ),那么可以直接用 HEAD~10 来表示。

我们接下来讲讲 reset 这个命令的选项:

reset命令的选项

git rest --mixed HEAD~(这是默认情况,直接写 git rest HEAD~ 就会默认加上 --mixer)

–移动HEAD的指向,将其指向上一个快照(这里就修改了第三棵树)

–将HEAD移动后指向的快照回滚到暂存区域(这里就修改了第二棵树)

为了更灵活的操纵这三棵树,git 还为 rest 命令安排了两个选项,--soft 和 --hard。软硬兼施,不到你不服。

首先是 --soft,使 reset 变得软:

•git rest --soft HEAD~

–移动HEAD的指向,将其指向上一个快照

这个命令就只影响了一棵树,就只是改变了第三棵树,改变了HEAD指针的指向,但不会更改暂存区里面的内容,暂存区里面依旧存放的是上一次提交的最新的内容。

该选项的作用相当于撤销上一次的提交(commit),比如说不小心提交了,后悔了,就可以直接 git rest --soft HEAD~,相当于撤销一次错误的 commit 命令。

接下来就是 --hard,就是使得 reset 变得硬。

•git rest --hard HEAD~

–移动HEAD的指向,将其指向上一个快照

–将HEAD移动后指向的快照回滚到暂存区域

–将暂存区域的文件还原到工作目录

这个命令不仅改变了 HEAD 的指向,影响了第三棵树,也同时影响了第二棵树,就是将指向的快照回滚到暂存区域,同时还会影响第一棵树,将暂存区域的文件还原到工作目录。所以呢,使用 hard 命令你就要注意了,这是有危险的,因为它会把你工作目录里的最新的文件给覆盖掉。

我们来演示一下 hard 命令:

我们现在的工作目录下有两个文件:LICENSE 和 README.md。

我们执行 git reset --hard HEAD~  

(刚才 HEAD 已经指向的是 7540e6c....,,所以现在再上一个就是 第一个版本 1fe46d....)

E:\MyProject>git reset --hard HEAD~
HEAD is now at 1fe46d0 add a README file

E:\MyProject>

Git实用教程 4.0:回到过去_第5张图片

LICENSE 也被删掉了。

所以我们说 git reset --hard HEAD~  这个命令是有危险的,它会把工作目录里面修改得很辛苦的文件给删除了。

现在再执行 git log:就只有一个了。

E:\MyProject>git log
commit 1fe46d092b4a7f1c99ddac65d7c2958000325f1e (HEAD -> master)
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:24:14 2019 +0800

    add a README file

E:\MyProject>

这时的情况就是下图所示:这就是hard 的威力,同时影响3棵树。

Git实用教程 4.0:回到过去_第6张图片

我们来总结一下,reset 命令回滚快照有三部曲:

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

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

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

除此之外,还可以回滚指定快照

比如说,你的快照比较多,你又懒得去数前面到底有多少个 “上”,你就可以通过指定具体的快照 ID 来回滚快照,我们知道, 快照 ID 是一个很长的唯一值,你只要指定前面几个数字让 git 可以识别就可以了

git reset 版本快照的ID号

回滚个别文件

reset 不仅可以回滚指定的快照,还可以回滚快照中的个别文件,命令如下:

git reset 版本快照 文件名/路径

这里需要注意的一点是:如果只是回滚个别个别文件的话,会忽略 第一步 HEAD 指针移动的这一步,因为你只是回滚快照中的个别文件,而不是回滚整个快照,所以 HEAD 指针不会改变。

另外:reset 不仅可以往回滚,还可以往前滚!

(不仅可以回到过去,还可以去到未来)命令如下:

git reset 版本快照的ID号

唯一的条件就是要知道 快照的 ID 号,但其实知道 ID 号也不是一件很难的事情,只需要 git log 就可以了。

E:\MyProject>git log
commit 1fe46d092b4a7f1c99ddac65d7c2958000325f1e (HEAD -> master)
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:24:14 2019 +0800

    add a README file

我们现在在这里,并且我们的 工作目录中的 LICENSE 也没有了,我们试图回到 最新的 版本,将我们 第二个版本的 LICENSE 弄回来。

E:\MyProject>git reset 08f7a42
Unstaged changes after reset:
D       LICENSE

E:\MyProject>git log
commit 08f7a42b90ff076d7f26fb6dbdbc9b86808256c0 (HEAD -> master)
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:28:24 2019 +0800

    change the LICENSE file

commit 7540e6c3dca0b17139054df7548fe0570fd741f2
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:27:03 2019 +0800

    add a LICENSE file

commit 1fe46d092b4a7f1c99ddac65d7c2958000325f1e
Author: XiangyangDai <[email protected]>
Date:   Mon Jan 14 21:24:14 2019 +0800

    add a README file

通过 git reset 08f7a42,就将 HEAD 指向了最新的版本快照。

但是我们在 工作目录中一看,我们的 LICENSE 还是没有出现啊。

问题在于你使用的是默认的 mixer 方式,mixer 的方式只是恢复到暂存区域,并没有把暂存区的内容恢复回工作目录。

我们可以通过 git status 查看:

E:\MyProject>git status
On branch master
Changes not staged for commit:
  (use "git add/rm ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        deleted:    LICENSE

no changes added to commit (use "git add" and/or "git commit -a")

E:\MyProject>

你可以通过 checkout 把它拉回工作目录。

事实上,我们直接加上 hard 也是一样的:

E:\MyProject>git reset --hard 08f7a42
HEAD is now at 08f7a42 change the LICENSE file

E:\MyProject>

你就会看到,我们的第二个版本的 LICENSE 又出现在了工作目录了。

那有的朋友又有问题了,我一时手快,把 cmd 命令窗口给关上了,那我们就不能往前拉,找到所有版本快照的 ID号了。

那怎么办才好呢?

事实上也是有办法的:git reflog

E:\MyProject>git reflog
08f7a42 (HEAD -> master) HEAD@{0}: reset: moving to 08f7a42
08f7a42 (HEAD -> master) HEAD@{1}: reset: moving to 08f7a42
1fe46d0 HEAD@{2}: reset: moving to HEAD~
7540e6c HEAD@{3}: reset: moving to HEAD~
08f7a42 (HEAD -> master) HEAD@{4}: commit: change the LICENSE file
7540e6c HEAD@{5}: commit: add a LICENSE file
1fe46d0 HEAD@{6}: commit (initial): add a README file

 

你可能感兴趣的:(Git,与,GitHub,Git)