git深入理解(五):git reset详解

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
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值