Reset current HEAD to the specified state
首先解释一下这句描述是什么意思,HEAD是指向某个commit的指针,既然如此,那么我们就可以操作HEAD来使其指向特定的commit。
关于暂存区(index)的知识 https://blog.csdn.net/raoxiaoya/article/details/110824019
关于什么是HEAD可阅读 https://blog.csdn.net/raoxiaoya/article/details/110862360
那么有什么用呢?
要知道每个commit对象保存着全量的tree结构,从这个tree可以恢复与之对应的index,而从index可以检出此时的文件到working directory。所以我们操作HEAD之后会有附加的功能,那就是恢复index,恢复working directory。所以 git reset 也就伴随有“回滚”,“撤销”的能力。
$ git reset -h
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
or: git reset [-q] [<tree-ish>] [--] <paths>...
or: EXPERIMENTAL: git reset [-q] [--stdin [-z]] [<tree-ish>]
or: git reset --patch [<tree-ish>] [--] [<paths>...]
-q, --quiet be quiet, only report errors
--mixed reset HEAD and index
--soft reset only HEAD
--hard reset HEAD, index and working tree
--merge reset HEAD, index and working tree
--keep reset HEAD but keep local changes
--recurse-submodules[=<reset>]
control recursive updating of submodules
-p, --patch select hunks interactively
-N, --intent-to-add record only the fact that removed paths will be added later
-z EXPERIMENTAL: paths are separated with NUL character
--stdin EXPERIMENTAL: read paths from <stdin>
主要参数
--soft reset only HEAD 设置HEAD
--mixed reset HEAD and index 设置HEAD,重置index
--hard reset HEAD, index and working tree 设置HEAD,重置index,检出working directory
默认是 --mixed
从暂存区取消刚才add的文件
git reset HEAD <file>
git 处于clean状态,查看暂存区种有那些文件
$ git ls-files
a/b/c/t3.txt
q/w/t4.txt
t1.txt
t2.txt
场景1
修改 t1.txt 文件,并 add,此时为带提交的状态,如果想 t1.txt 恢复到 Unstage 状态,可以使用如下命令撤销此次 add 。
$ git reset HEAD t1.txt
Unstaged changes after reset:
M t1.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: t1.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git ls-files
a/b/c/t3.txt
q/w/t4.txt
t1.txt
t2.txt
这个场景个人感觉没什么价值,因为add之后blob文件已经生成了,而如果你想接着修改你完全可以再add一次,最后再提交;
场景2
在一个新项目的时候,我们往往会先将不需要跟踪的文件加入到 .gitignore 中,但是它的弊端在于只要是存在于 Index 中的文件,即使你将它加入到 .gitignore 中,它还是会被跟踪的,所以这个 .gitignore 只适合用在初始化项目的时候。
好,假设你第一次
将某个文件add,还没有commit,此时你发现这个文件应该被 ignore ,此时可以使用 git reset HEAD filename
来将其移出index。然后在 .gitignore 中标记。可见这个命令依然比较鸡肋。
先不管它有没有价值,来思考一下它的原理是什么?
还是回到上面的解释,git reset HEAD filename 虽然没有移动HEAD,但是git还是会取执行后续的操作,因为默认模式是 --mixed ,也就是需要恢复 index,所以 index 就被恢复到上一次commit时的状态了。
从 index 中删除某个文件的命令应该是 git rm --cached filename
版本回滚
如何指定回滚到哪里
HEAD 表示当前版本
HEAD^ 上一个版本
HEAD^^ 上上一个版本
HEAD~100 上100个版本,通用的
版本号 指定版本
可以配合不同的模式(–mixed, --soft, --hard)达到不同的效果。
首先查看commit日志
$ git log --pretty=oneline
1341074157aeb92de8caf982507d3f6c9280d5eb (HEAD -> master) commit 03
86b5d83fe42a1a9da8e96612a6c2c91f8e3e2001 commit 02
23dddd7b12606de10ed759cb72793d072ef2e48a commit 01
faa4214bc342ade5693a7efc8a64e869965c039e fix conflict
818c5faf28d0a0e5c8133dbd77dd24e6e70db9bf aaaaaaa
6f43203cf463dc5320916f96abef0f1ad63428fd (b1) xx
adda355046920ae91118cf42ec2f45190b0ec89c test
2e1b4bced0f0ce2c20362789be2878b36c6910f7 add t4
8262ea4e39ea80dc56056a667e9dbdcd235efc08 add t3
f2b85bf7f7516a6a6a0768e44266d09414b03a2e 2
01d308a7ef190b881969ea9b9112424819ab346a first commit
commit 01, commit 02, commit 03 为最近的三次提交,是提交时的备注信息。
HEAD -> master
表示当前HEAD处于 master分支的1341074157aeb92de8caf982507d3f6c9280d5eb
我们回到上一个版本 commit 02 去,也就是 86b5d83fe42a1a9da8e96612a6c2c91f8e3e2001
$ git reset --hard HEAD^
HEAD is now at 86b5d83 commit 02
或者 git reset --hard HEAD~1 或者 git reset --hard 86b5d83fe42a1a9da8e96612a6c2c91f8e3e2001
发现代码发生了变化,和预期一直。
再使用 git log 查看一下
$ git log --pretty=oneline
86b5d83fe42a1a9da8e96612a6c2c91f8e3e2001 (HEAD -> master) commit 02
23dddd7b12606de10ed759cb72793d072ef2e48a commit 01
faa4214bc342ade5693a7efc8a64e869965c039e fix conflict
818c5faf28d0a0e5c8133dbd77dd24e6e70db9bf aaaaaaa
6f43203cf463dc5320916f96abef0f1ad63428fd (b1) xx
adda355046920ae91118cf42ec2f45190b0ec89c test
2e1b4bced0f0ce2c20362789be2878b36c6910f7 add t4
8262ea4e39ea80dc56056a667e9dbdcd235efc08 add t3
f2b85bf7f7516a6a6a0768e44266d09414b03a2e 2
01d308a7ef190b881969ea9b9112424819ab346a first commit
发现 commit 03 已经看不到了,为什么呢?因为每一个commit只会保存它的parent节点,并不知道它的下一个节点时什么。那么问题来了,我又想回到 commit 03 该怎么办呢?
Git提供了一个命令git reflog
用来记录你的每一次命令
$ git reflog
86b5d83 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
1341074 HEAD@{1}: reset: moving to 1341074157aeb92de8caf982507d3f6c9280d5eb
86b5d83 (HEAD -> master) HEAD@{2}: reset: moving to HEAD
86b5d83 (HEAD -> master) HEAD@{3}: reset: moving to HEAD^
1341074 HEAD@{4}: commit: commit 03
86b5d83 (HEAD -> master) HEAD@{5}: commit: commit 02
23dddd7 HEAD@{6}: commit: commit 01
找到 commit 03 这一条,1341074 就是第一个版本号。
$ git reset --hard 1341074
HEAD is now at 1341074 commit 03
哈哈哈,我又回来了。
暴力清扫
如果出现以下情况 Your branch and 'origin/master' have diverged
> git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
然后我执行
> git pull
warning: Cannot merge binary files: app/vote2/vote2 (HEAD vs. 81262ac89253117b592d89f4f0d6419c6ec74730)
Auto-merging app/vote2/vote2
CONFLICT (content): Merge conflict in app/vote2/vote2
Automatic merge failed; fix conflicts and then commit the result.
可以选择强制和线上同步
> git reset --hard origin/master
HEAD is now at 81262ac Refactor: 投递日志优化。
> git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean