git各种撤回操作

关于使用git过程中关于撤回的操作的一些记录,涉及到三个指令,checkoutresetrevert

代码状态

任何的文件修改(包括增删等)都会经历四个阶段:

  • 工作目录(Working Directory):所进行的任何改动,都是工作区中的改动

  • 暂存区(Index):文件改动通过add指令进入暂存区;

  • HEAD:HEAD可以理解为一个指针,通常会指向当前分支的最后一次提交(也可以指向当前分支的历史提交)。Index中的文件改动通过执行commit完成提交;

  • 远端仓库:本地提交记录,通过push推送到远端仓库;

撤回操作

对于处于不同状态的文件,撤回的操作也完全不同,先从我们经常看到的一句提示说起。在输入git status之后,可能会看到下边的提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: file1.txt

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: file2.txt

大概意思就是说,将要被commit的修改,有file1,可以通过git reset ...来unstage(即从暂存区Index移除回到工作区)。对于file2,可以使用add将其暂存,或者checkout丢弃工作区中的所有修改。下边就主要记录一下关于checkoutreset的一些操作,以及保留提交历史记录的撤回revert指令(如果已经把代码修改推到远端仓库就必须使用这个撤回了)。

checkout系列

checkout指令的作用是切换分支或撤回工作区中的修改。下边是一些经常会用到的指令组合:

1
git checkout <branch>

指定分支的名字,切换到对应的分支,如果此时工作区有代码修改,可以stash或者先提交。如果是无关的改动,并不会在checkout时将改动丢弃。

1
git checkout -- <file>

对于指定的文件,丢弃工作区中的所有修改,将其撤回到同HEAD所在的commit的版本状态。

1
git checkout <commit> <file>

对于指定的文件,丢弃工作区中的所有修改,将其撤回到的版本状态。

可以使用HEAD~HEAD~1表示HEAD的前一次提交(HEAD的第一个父节点),使用HEAD~2表示HEAD向前的第二次提交(HEAD的第一个父节点的第一个父节点),等等。

可以使用HEAD^HEAD^1表示HEAD的前一次提交(HEAD的第一个父节点),如果当前的HEAD是由一次merge产生,可以使用HEAD~2表示HEAD的第二个父节点提交版本,等等。

1
git checkout .

对于当前工作区中的所有文件,丢弃修改,将其撤回到HEAD所在的commit的版本状态。

reset系列

reset用于将当前HEAD置为指定的状态(target),配合不同的参数可以实现不同的对文件状态的操作,这里直接使用文档里给的示例。A、B、C和D分别表示的是文件的四个状态。其中target代表想要将HEAD指向到哪个commit,命令行参数中的<commit>,working、index、HEAD分别就是工作区、暂存区和HEAD

使用git reset命令时,常常可以配合参数softmixedhard,如果缺省的话相当于使用的是mixed,以下是一些命令对文件状态修改的示例,注意这里都是只使用了一个参数<commit>(缺省为HEAD),没有配合<file>参数:

1
2
3
4
5
working index HEAD target         working index HEAD
----------------------------------------------------
A B C D --soft A B D
--mixed A D D
--hard D D D
1
2
3
4
5
working index HEAD target         working index HEAD
----------------------------------------------------
A B C C --soft A B C
--mixed A C C
--hard C C C
1
2
3
4
5
working index HEAD target         working index HEAD
----------------------------------------------------
B B C D --soft B B D
--mixed B D D
--hard D D D
  • soft只会影响到HEAD,它将HEAD指向指定的target;
  • mixed除了将HEAD指向target之外,还会将暂存区中的文件状态重置为新的HEAD的状态;
  • hardHEAD指向target,然后将暂存区和工作区都重置为新的HEAD的状态;

注意注意,上边这么多说的都是不带<file>参数的情况,如果参数中指定了文件,则相当于对暂存区中的该文件的状态执行了重置,此时不会修改HEAD指向的位置(即使指定了别的<commit>),只修改暂存区,并且保留工作区中的文件状态。

用一个表格来整理一下这一组命令,其中模式一列如果空缺表示--mixed,target一列如果空缺表示HEAD

命令 模式 target参数 文件参数 HEAD 工作区 暂存区
git reset --soft <commit> 指向target 保留状态 保留状态
git reset --mixed <commit> 指向target 保留状态 和新的HEAD一致
git reset --hard <commit> 指向target 和新的HEAD一致 和新的HEAD一致
git reset --mixed <commit> <file> 不变 保留状态 和target一致
git reset --mixed <commit> <file> 不变 保留状态 和target一致

revert系列

保证之前提交历史记录的基础上,创建新的提交记录,其中的改动为对应的<commit>中的逆操作:

1
git revert <commit>

REFERENCE

https://git-scm.com/docs/git-reset

https://git-scm.com/docs/git-checkout

https://git-scm.com/docs/git-revert