因为平时使用 SmartGit 这样一个 Git client,所以也没有太大注意 Git 中不同阶段撤回的方式,虽然平时接触过 git reset--soft--hard 来撤销已提交的 commit,但没有形成一个系统的知识体系。大家都知道 Git 是一个分布式版本控制,所以 Git 会有一个本地库,和一个远端库,而平时提交代码的时候,一般也都是先从本地工作区提交代码

git add .
git commit -s
git push

这几个步骤,虽然平淡无奇,但是展开说,就体现了 Git 的重要的环节,一段代码的提交顺序:

工作区  ->  git add .  -> 暂存区 -> git commit -> 本地仓库 -> git push -> 远程仓库

这里就要提到 Git 中的四个区:

  • 工作区 working
  • 暂存区 stage
  • 本地仓库 local repository
  • 远程仓库 remote repository

被追踪的文件,在未进入和进入上述四个区之后分别有一个状态,所以一共有五个状态:

  • 未修改 origin
  • 已修改 modified
  • 已暂存 staged
  • 已提交 committed
  • 已推送 pushed

在了解这几个基本概念之后,如何检查本地的修改,以及如何查看不同状态之间的修改,这就要用到 git diff 命令。

直接使用 git diff 命令,能够查看已修改,未暂存的内容

使用 git diff --cache 来查看已暂存,未提交的内容

使用 git diff origin/master master 来查看已提交,未推送的差异。

工作区          暂存区           本地仓库                    远程仓库
    \          /     \          /         \                  /
     \        /       \        /           \                /
     git diff         git diff --cache     git diff origin/master master

图解

git-workspace-index

在知道如何查看四个不同区之间的差异后,如何使用 git reset 来撤销呢?

撤销工作区修改

如果只是在编辑器中修改了文件的内容,还未使用 git add 将修改提交到暂存区,那么可以使用 git checkout . 或者 git checkout -- <file> 来丢弃本地全部修改或者丢弃某文件的修改。

可以将 git add .git checkout . 看做一对反义词,修改完成后,如果想 Git 往前进一步,让修改进入暂存区,执行 git add . 如果向后退则执行 git checkout .

撤销暂存区修改

如果已经执行了 git add,意味着暂存区中已经有了修改,但是需要丢弃暂存区的修改,那么可以执行 git reset

对于已经被 Git 追踪的文件,可以使用

git reset <file>

来单独将文件从暂存区中丢弃,将修改放到工作区。

对于从来没有被 Git 追踪过,是 new file 的文件,则需要使用:

git reset HEAD <file>

来将新文件从暂存区中取出放到工作区。

如果确定暂存区中的修改完全不需要,则可以使用

git reset --hard

直接将修改抛弃,谨慎使用 –hard 命令, 暂存区中所有修改都会被丢弃。修改内容也不会被重新放到工作区。

撤销本地提交

对于已经本地的提交,也就是使用 git add 并且执行了 git commit 的修改,这时候本地的修改已经进入了本地仓库,而这是需要撤销这一次提交,或者本地的多次提交,怎么办?

git reset --hard origin/master

同样还是 git reset 命令,但是多了 origin/masterorigin 表示远端仓库的名字,默认为 origin,可能也有其他自己的名字,origin/master 表示远程仓库,既然本地的修改已经不再需要,那么从远端将代码拉回来就行。

不过不建议直接使用 git reset --hard origin/master 这样太强的命令,如果想要撤销本地最近的一次提交,可以使用

git reset --soft HEAD~1

这行命令表示,将最近一次提交 HEAD~1 从本地仓库回退到暂存区,--soft 不会丢弃修改,而是将修改放到暂存区,后续继续修改,或者丢弃暂存区的修改就可以随意了。如果要撤销本地两次修改,则改成 HEAD~2 即可,其他同类。

不过要注意的是,已经提交到远端的提交,不要使用 git reset 来修改,对于多人协作项目会给其他人带来很多不必要的麻烦。

撤销远端仓库修改

对于已经推送的修改,原则上是不要撤销的,不过 Git 给了使用者充分的自由,在明确自己在做什么的情况下,可以使用 git push -f 使用 force 选项来将本地库 force 覆盖远端仓库,强制 push 到远端。

对于个人,一个人使用的项目使用这样的方式,并没有太大问题,但是如果对于多人项目,如果你强行改变了远端仓库,别人再使用的时候就会出现很多问题,所以使用 git push -f 时一定要想清楚自己在做什么事情。