Git实用教程 4.1:回到过去2

小时候歌儿有得唱“想回到过去 / 试着抱你在怀里 / 羞怯的脸带有一点稚气 / 想看你看的世界 / 想在你梦的画面 / 只要靠在一起 / 就能感觉甜蜜”

小时候让你不抓紧,整天撸啊撸,玩 DOTA,长大你就知道后悔!

也罢也罢,伤心事儿咱不提了……

人生没有后悔药,Git 则是一部设计精良的时光机器。

(这不废话嘛,版本控制说白了就是可以回到以前的版本快照)

上一讲我无意中接触到了两个有关回退的命令:reset 和 checkout

来,帮你捋一捋思路:

现在几个命令应该相当清晰了:

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

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

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

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

这里记录了我们之前的 3 次提交(排序是按时间从近到远的),Author 后边是提交者,Date 后边是提交日期,下边是当次提交的说明。那……草黄色的那个 commit +“乱码”是什么鬼?

咳咳~~

这里并没有什么“乱码”,这个是 Git 为每次提交计算出来的 ID,它其实一个完整的 SHA-1 校验和(尽管你的文件内容可能跟我的完全一致,但这个值却不一样,这是因为账号、时间不同而导致)。你不需要知道 SHA-1 的原理,只需要知道它在任何时候都是唯一的,通过这个 ID,你就可以找到对应的那个版本。

有朋友可能会问:为何 ID 不是传统的 1、2、3、4、5…… 难道作者是为了装 X?

非也非也,因为 Git 是分布式的版本控制系统,如果多人同时工作,那么使用 1、2、3、4、5 就很容易产生冲突,继而打群架……

根据 log 记录,现在我们将 Git 仓库如果可视化,应该是这样子:

三棵树现在应该是下面酱紫:

回滚快照

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

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

执行 git reset HEAD~ 命令:

注:HEAD 表示最新提交的快照(08f7a42),而 HEAD~ 表示 HEAD 的上一个快照(754016c)

然后执行 git status 命令查看现在的状态:

有哪位童鞋可以回答我:现在我们的快照(754016c)回滚到了哪一棵树里?

 

答案是:第二棵树(暂存区域)!

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

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

好了,现在执行完 git reset HEAD~ 命令之后,Git 仓库应该是这样子:

三棵树现在应该是下面酱紫:

这里有一点要补充的:HEAD~ 表示 HEAD 的上一个快照(7540e6c),HEAD~~(1fe46d)则表示 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 reset --hard HEAD~ 命令:

Git 仓库中就剩下最后一个快照了:

还原案发现场,Git 仓库现在应该是这样:

而三棵树现在应该都被回归到第一个版本(1fe46d):

不信?自己瞧瞧你的文件夹:

LICENSE 文件没了

最后总结一下:reset 回滚快照三部曲

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

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

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

回滚指定快照

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

比如 git reset 00c2929

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

回滚个别文件

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

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

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

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

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

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

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

但是将命令行窗口向上拉,我们可以喵到之前提交的几个版本 ID 号(聪明如我 ):

所以我们可以执行 git reset --hard 31f46be 命令:

再次执行 git log 命令:

我们又回到了最新的版本!

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

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

reflog 命令可以拯救你!

执行 git reflog 命令,告诉我,你看到了什么:

没错,Git 偷偷记录下了你每一次的操作(无耻小人),第一列就是每次执行完命令,HEAD 指向的版本 ID 号啦~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值