vlambda博客
学习文章列表

Git出售的后悔药(上)

本篇内容如下:

  • Git的分区和提交

  • 未提交时的撤回

一、Git的分区和提交

虽然概念很枯燥,但是如果不理解概念而是死记硬背会更枯燥。

1.一张图

Git分三个区:

  • 工作区

  • 历史提交

  • 暂存区


下面这张图展示了工作区、 历史提交、stage区之间的关系。

先看张图留点印象,接下来有详细介绍。

2.介绍分区

工作区

工作区又叫working directory。我们直接编辑代码文件,就是在编辑的工作区的代码。

这个最简单,最直观。

历史提交

我们每次 commit 操作就会构造一个历史提交。通过 git log看到的就是历史提交,每个commit都记录了提交时的文件的状态。每个提交不会记录整个文件,只是记录了根据上一次的改动。

我们习惯用 commitId 指代某次提交。而所谓的分支(branch)、tag都是指向 commitId的指针。

此外还有一个特殊的指针 HEAD无论我们在哪个分支,它总是指向了当前分支下的最新一个commitId。所以我们会 用HEAD指代最新提交。

暂存区

暂存区,又叫Stage区、index区。平时我们执行 git add就是把工作区的文件放到 stage 区,作为我们下一次提交。

git add 操作的文件是工作区的文件,而 git commit 操作的对象就是 stage 区的文件。

一般情况下 stage 区和 HEAD 指向的文件是一样的。

如果我们先把某文件add 到stage区,再去修改这个文件,那么这个时候 暂存区、工作区、HEAD,三个区的文件都不一样。

注意区分,是stage区,不是 stash 区,stage跟 git stash没关系

git diff

git diff 比较的是 stage和工作区的区别。所以如果执行了 git add 之后,再执行 git add 是看不到区别的。这时候要加个参数 --cached

git diff--cached 比较的是 stage区和HEAD的区别。

git diff可以指定要对比的文件或目录,否则会列出所有文件的区别。

二、未提交时的撤回

1.两张图

这两张图是以前网上找的,因为实在是太精辟了,忍不住保存了。

分步操作


合并操作

撤销,可以理解为用一个区的文件覆盖另一个区的文件。

接下来结合图梳理应用场景。

2.未add,使用checkout

如果还没有执行 git add,我们不想要当前的修改了。

此时stage区和HEAD的文件是一致的。我们用 stage 区的文件覆盖工作区的文件即可:

git checkout--<file1><file2>

如果想撤销所有文件的改动,又不想一个个输入文件名,可以指定目录

  • 当前目录 git checkout--.

  • 指定目录 git checkout--path/to/dir

3.已add但未commit,用reset

reset这个指令有点危险,但却是支持git回滚的核心指令。

这种情况可以分两步走,先用历史提交覆盖stage,再用stage覆盖工作区,如分步操作图中左边的指令:

 
   
   
 
  1. git reset -- .

  2. git checkout -- .

也可以一步到位直接用历史提交同时覆盖stage区和工作区,如合并操作图中左边的指令:

 
   
   
 
  1. git checkout HEAD -- .

此外,以下指令是等效的,默认reset的参数就是 HEAD 和当前目录。

  • git reset

  • git reset--

  • git reset--.

  • git reset HEAD

  • git reset HEAD--

  • git reset HEAD--.

都是用 HEAD 的文件来覆盖 stage区。当stage区恢复到了最后一次提交,我们工作区还是修改中的状态。

这里的HEAD还可以替换成历史提交的commitId、Tag、分支名,那么就会用他们指向的 commitId 中的文件来覆盖工作区了,这是高级操作,请一定知道自己在做什么。

因为 checkout 也是切换分支的指令,所以后面需要有一个 -- 标识操作的是文件或目录。当然现在高版本的 git 比较智能,一般情况下不加该标识也能知道我们是要切分支还是回滚文件。如果遇到文件名和分支冲突的时候,git会做出提示。

三、预告

明天写这些:

  • 已提交的回滚

  • 区分 reset和revert

  • 撤回被reset掉的提交