前言
前面了解过,每次提交都会产生新的id,master引用分支总是会指向最新的提交id,其实,引用也可以指向历史id,将当前版本指向历史id,这就是重置。
引用就像是一个游标,可上可下。git提供了reset命令来指向任意一个id。
reset
git reset
命令就是用来重置的命令,利用这个命令可以回退到想要的版本,下面可以试验一下
先创建一个新文件,并提交
$ touch newfile
$ git add newfile
$ git commit -m "does master follow this new commit?"
[master 6a26023] does master follow this new commit?
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 newfile
$ ls
hellogit newfile
查看master引用指向的id
$ cat .git/refs/heads/master
6a2602307968a2fa13efc3974ab4e0f4e7abf49a
确实指向了新的提交id,查看一下日志
$ git log --oneline
6a26023 (HEAD -> master) does master follow this new commit?
6cffe14 2018.4.20 change1
5782fe2 third change
0789463 which version checked in?
ac01ff3 init hellogit file
现在想将版本重置到父提交
$ git reset --hard HEAD^
HEAD is now at 6cffe14 2018.4.20 change1
再查看master引用
$ cat .git/refs/heads/master
6cffe145e4f83255fbdef7f68c4b0ed94acea8bc
确实指向了父提交了,再看下日志
$ git log --oneline
6cffe14 (HEAD -> master) 2018.4.20 change1
5782fe2 third change
0789463 which version checked in?
ac01ff3 init hellogit file
head和master引用都指向了父提交,再看一下文件是否存在
$ ls
hellogit
文件也删了
......
看了上面的日志,是完成了目的,回退到父提交了,但是,是不是有点太干净了,没有任何的回退记录,那如果是错误的回退怎么办,连日志都没了!!!这是一个危险的操作(特别是--hard参数,会破坏工作区未提交的改动),会彻底的丢弃历史,所以是不可能通过提交历史的办法来恢复,所以慎重!!!!
reflog
git作为一个这么x的版本控制工具,岂会不考虑错误重置的这种情况。reflog就是来挽救错误的reset。
在.git/logs
目录下记录了分支的变更(必须是带有工作区的提供分支日志功能)。这是因为带有工作区的版本库都有以下设置
$ git config core.logallrefupdates
true
查看一下master分支的日志文件
$ tail -5 .git/logs/refs/heads/master
ac01ff39a78fa388e1e628754fd55a76feb358d2 0789463b17be77ffe8da6fbc7e4b7777d065008c smxknife <2323937771@qq.com> 1522171550 +0800 commit: which version checked in?
0789463b17be77ffe8da6fbc7e4b7777d065008c 5782fe225003c2bc481d554ddeeab8fecd0dd19d smxknife <2323937771@qq.com> 1522171984 +0800 commit: third change
5782fe225003c2bc481d554ddeeab8fecd0dd19d 6cffe145e4f83255fbdef7f68c4b0ed94acea8bc smxknife <2323937771@qq.com> 1524190757 +0800 commit: 2018.4.20 change1
6cffe145e4f83255fbdef7f68c4b0ed94acea8bc 6a2602307968a2fa13efc3974ab4e0f4e7abf49a smxknife <2323937771@qq.com> 1524318936 +0800 commit: does master follow this new commit?
6a2602307968a2fa13efc3974ab4e0f4e7abf49a 6cffe145e4f83255fbdef7f68c4b0ed94acea8bc smxknife <2323937771@qq.com> 1524323213 +0800 reset: moving to HEAD^
在这里可以找到最后的操作日志,最后一条,reset:moving to HEAD^,提交id由6a26变为6cff
除了直接查看文件外,还可以通过reflog命令来查看
$ git reflog show master | head -5
6cffe14 master@{0}: reset: moving to HEAD^
6a26023 master@{1}: commit: does master follow this new commit?
6cffe14 master@{2}: commit: 2018.4.20 change1
5782fe2 master@{3}: commit: third change
0789463 master@{4}: commit: which version checked in?
可以看到同样的效果,只是对日志里面的信息进行精简和包装。在reflog命令中提供了一个方便易记的表达式:refname@{n},上面输出的就是master@{0}。这个表达式的意思是refname之前第n次改变时的id值。那么将master切换之前的值,可以使用下面的命令
$ git reset --hard master@{1}
HEAD is now at 6a26023 does master follow this new commit?
查看日志
$ git log --oneline
6a26023 (HEAD -> master) does master follow this new commit?
6cffe14 2018.4.20 change1
5782fe2 third change
0789463 which version checked in?
ac01ff3 init hellogit file
查看引用
$ cat .git/refs/heads/master
6a2602307968a2fa13efc3974ab4e0f4e7abf49a
查看工作区文件
$ ls
hellogit newfile
用reflog命令查看日志
$ git reflog show master | head -5
6a26023 master@{0}: reset: moving to master@{1}
6cffe14 master@{1}: reset: moving to HEAD^
6a26023 master@{2}: commit: does master follow this new commit?
6cffe14 master@{3}: commit: 2018.4.20 change1
5782fe2 master@{4}: commit: third change
深入了解git reset命令
$ echo "this is new" >> newfile
$ git status -s
M newfile
$ git diff
diff --git a/newfile b/newfile
index e69de29..fff92cf 100644
--- a/newfile
+++ b/newfile
@@ -0,0 +1 @@
+this is new
$ git add newfile
$ git status -s
M newfile
$ git diff
$ git reset HEAD
Unstaged changes after reset:
M newfile
$ git status -s
M newfile
$ git diff
diff --git a/newfile b/newfile
index e69de29..fff92cf 100644
--- a/newfile
+++ b/newfile
@@ -0,0 +1 @@
+this is new
从上面的操作来看,git reset HEAD 并没有改变工作区。改变的只有暂存区文件。将git add newfile 重置为add之前的状态。如果get reset HEAD^^ 就会发现引用也被改变,暂存区也被改变,只有工作区没有变化
--hard
使用hard模式会执行以下三个操作:
- 替换引用的指向。引用指向新的提交id
- 替换暂存区。替换后,暂存区的内容和引用指向目录树一致
- 提供工作区。替换后,工作区的内容变得和暂存区一致(引用、暂存区、工作区状态一致)
--soft
soft模式会执行一个操作:
- 更改引用的指向,不改变暂存区和工作区
--mixed
mixed模式同上面的git reset HEAD~n
- 将引用和暂存区回退到执行提交,工作区不变
关于内部类的详细文章可以参考目录 [smxknife's Java内部类]