• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

git操作(7):reset

Git winrains 来源:veedrin 1年前 (2019-08-30) 56次浏览

git checkout命令可以在版本之间随意切换,它的本质是移动HEAD指针。
那git有没有办法移动分支指针呢?
当然有,这就是git reset命令。

底层

git reset命令与git checkout命令的区别在于,它会把HEAD指针和分支指针一起移动,如果HEAD指针指向的是一个分支指针的话。
我们前面说过使用git checkout命令从有分支指向的commit切换到一个没有分支指向的commit上,这个时候的HEAD指针被称为detached HEAD。这是非常危险的。

C0 -- C1 -- C2(HEAD -> master)
$ git checkout C1
C0 -- C1(HEAD) -- C2(master)

但是git reset命令没有这个问题,因为它会把当前的分支指针也带过去。

C0 -- C1 -- C2(HEAD -> master)
$ git reset C1
C0 -- C1(HEAD -> master) -- C2

这就是重置的含义所在。它可以重置分支。
看另一种情况。如果是从一个没有分支指向的commit切换到另一个没有分支指向的commit上,那它们就是两个韩国妹子,傻傻分不清楚了。
这是git checkout命令的效果。

C0 -- C1 -- C2(HEAD) -- C3(master)
$ git checkout C1
C0 -- C1(HEAD) -- C2 -- C3(master)

这是git reset命令的效果。

C0 -- C1 -- C2(HEAD) -- C3(master)
$ git reset C1
C0 -- C1(HEAD) -- C2 -- C3(master)

同时重置暂存区和工作区的改动

当你在 git reset 命令后面加 --hard 参数时,暂存区和工作区的内容都会重置为重置后的commit内容。也就是说暂存区和工作区的改动都会清空,相当于撤销暂存区和工作区的改动。
而且是没有确认操作的哟。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  modified:   a.md
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:   a.md
$ git reset --hard HEAD^
HEAD is now at 58b0040 commit for nothing
$ git status
On branch master
nothing to commit, working tree clean

仅重置暂存区的改动

git reset 命令后面加 --mixed 参数,或者不加参数,因为--mixed参数是默认值,暂存区的内容会重置为重置后的commit内容,工作区的改动不会清空,相当于撤销暂存区的改动。
同样也是没有确认操作的哟。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  modified:   a.md
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:   a.md
$ git reset HEAD^
Unstaged changes after reset:
M	a.md
$ 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:   a.md
no changes added to commit (use "git add" and/or "git commit -a")

打个趣,如果git reset命令什么都不加会怎样呢?
你可以脑补一下,git reset命令不加参数默认就是--mixed,不加操作对象默认就是HEAD,所以单纯的git reset命令相当于git reset --mixed HEAD命令。
那这又意味着什么呢?
这意味着从当前commit重置到当前commit,没有变化对吧?但是--mixed参数会撤销暂存区的改动对不对,这就是它的效果。

同时保留暂存区和工作区的改动

如果 git reset 命令后面加 --soft 参数,钢铁直男的温柔,你懂的。仅仅是重置commit而已,暂存区和工作区的改动都会保留下来。
更温柔的是,重置前的commit内容与重置后的commit内容的diff也会放入暂存区。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  modified:   a.md
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:   a.md
$ git diff --staged
diff --git a/a.md b/a.md
index 4a77268..fde8dcd 100644
--- a/a.md
+++ b/a.md
@@ -1,2 +1,3 @@
 apple
 banana
+cherry
$ git reset --soft HEAD^
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  modified:   a.md
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:   a.md
$ git diff --staged
diff --git a/a.md b/a.md
index 4a77268..fde8dcd 100644
--- a/a.md
+++ b/a.md
@@ -1 +1,3 @@
 apple
+banana
+cherry

banana就是重置前的commit内容与重置后的commit内容的diff,可以看到,它已经在暂存区了。

文件暂存区内容撤回工作区

git reset命令后面也可以跟文件名,它的作用是将暂存区的内容重置为工作区的内容,是git add -- <file>的反向操作。
git reset -- <file>命令是git reset HEAD --mixed -- <file>的简写。在操作文件时,参数只有默认的--mixed一种。
它并不会撤销工作区原有的改动。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  modified:   a.md
$ git reset -- a.md
Unstaged changes after reset:
M	a.md
$ 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:   a.md
no changes added to commit (use "git add" and/or "git commit -a")

git checkout命令后面也可以跟文件名,它的作用是撤销工作区的改动,需要注意区分。

文件若干commit版本撤回工作区

如果git reset命令后跟一个commit校验和,它会把该commit与所有后代commit的diff重置到工作区。
意思就是将该文件重置回你指定的commit版本,但是在你指定的commit之后的改动我也给你留着,就放到工作区里吧。

$ git diff --staged
# 空
git reset HEAD~4 -- a.md
Unstaged changes after reset:
M	a.md
$ git diff --staged
diff --git a/a.md b/a.md
index 6f195b4..72943a1 100644
--- a/a.md
+++ b/a.md
@@ -1,5 +1 @@
 aaa
-bbb
-ccc
-ddd
-eee

git diff --staged命令比较工作区和暂存区的内容。可以看到初始工作区和暂存区是一致的,重置文件到4个版本之前,发现工作区比暂存区多了很多改动,这些都是指定commit之后的提交被重置到工作区了。

作者:veedrin

来源:https://github.com/veedrin/horseshoe/blob/master/git/reset.md


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)