本节主要知识点:
回滚相关命令
git add
命令用于把工作目录的文件放入暂存区域git commit
命令用于把暂存区域的文件提交到 Git 仓库git reset
命令用于把 Git 仓库的文件还原到暂存区域git checkout
命令用于把暂存区域的文件还原到工作目录
reset 回滚快照三步曲
- 移动 HEAD 的指向(–soft)
- 将快照回滚到暂存区域([–mixed],默认)
- 将暂存区域还原到工作目录(–hard)
回滚指定快照
在命令行中将路径切换至文件夹中,然后使用命令
git reset + 具体的快照 ID(一般5位就行)'
回滚个别文件
在命令行中将路径切换至文件夹中,然后使用命令
git reset 快照 文件名/路径 '
reset 不仅是一个“复古”的命令,它不仅可以回到过去,还可以去到“未来”。不仅可以往回滚,还可以往前滚!
失去了以前版本的ID
在命令行中将路径切换至文件夹中,然后使用命令
git reflog'
上一讲无意中接触到了两个有关回退的命令: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 校验和(尽管你的文件内容可能跟我的完全一致,但这个值却不一样,这是因为账号、时间不同而导致),在任何时候都是唯一的,通过这个 ID,你就可以找到对应的那个版本。
根据 log 记录,现在我们将 Git 仓库如果可视化,应该是这样子:
回滚快照
注:快照即提交的版本,每个版本我们称之为一个快照。
现在我们利用 reset 命令回滚快照,并看看 Git 仓库和三棵树分别发生了什么。
执行 it reset HEAD~
命令:
注:HEAD 表示最新提交的快照(31f46),而 HEAD~ 表示 HEAD 的上一个快照(d19e3)
然后执行 git status
命令查看现在的状态:
现在我们的快照(d19e3)回滚到 第二棵树(暂存区域)!
有些朋友可能会持不同意见:不应该是回滚到第一棵树(工作目录)吗?你看,Git 不是写得很清楚吗 -> Changes not staged for commit,它还好心提醒我们使用 add 命令将修改添加到暂存区域丫!
其实真相是这样的:我们执行 git reset HEAD~
命令之后,快照(d19e3)回滚到暂存区域,此时工作目录里存放的却是最新的文件(31f46)。由于 Git 会跟踪文件的变化,所以执行 git status
命令时,git 发现工作目录中的文件比暂存区域的要新(对比日期),所以才有这样的提示……
好了,现在执行完 git reset HEAD~
命令之后,Git 仓库应该是这样子:
三棵树现在应该是下面酱紫:
这里有一点要补充的: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 reset --hard HEAD~
命令:
Git 仓库中就剩下最后一个快照了:
还原案发现场,Git 仓库现在应该是这样:
而三棵树现在应该都被回归到第一个版本(00c2929):
不信?自己瞧瞧你的文件夹:
最后总结一下:reset 回滚快照三部曲
-
移动 HEAD 的指向(–soft)
-
将快照回滚到暂存区域([–mixed],默认)
-
将暂存区域还原到工作目录(–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 号啦~