7.Git 的原理

一、前言

Git 的三个分区分别是:working directorystage/index areacommit history
image-20201212190410230

working directory 是「工作目录」,也就是我们肉眼能够看到的文件,后文我们称其为 work dir 区。
当我们在 work dir 中执行 git add 相关命令后,就会把 work dir 中的修改添加到「暂存区」stage area(或者叫 index area)中去,后文我们称暂存区为 stage 区
当 stage 中存在修改时,我们使用 git commit 相关命令之后,就会把 stage 中的修改保存到「提交历史」 commit history 中,也就是 HEAD 指针指向的位置。后文我们称「提交历史」为 history 区
关于 commit history 我们多说几句,任何修改只要进入 commit history,基本可以认为永远不会丢失了。每个 commit 都有一个唯一的 Hash 值,我们经常说的 HEAD 或者 master 分支,都可以理解为一个指向某个 commit 的指针。
work dirstage 区域的状态,可以通过命令 git status 来查看,history 区域的提交历史可以通过 git log 命令来查看。

image-20201212190410230

二、使用

1.如何把 work dir 中的修改加入 stage

使用 git add 相关的命令(add 有个别名叫做 stage,git add ==> git stage)。
风险等级:无风险。
理由:不会改变任或撤销任何已作出的修改,而且还会将 work dir 中未追踪的修改(Untracked file)添加到暂存区 stage 中进行追踪。

2.如何把 stage 中的修改还原到 work dir 中

比如我先将当前 work dir 中的修改添加到 stage 中,然后又对 work dir 中的文件进行了修改,但是又后悔了,如何把 work dir 中的全部或部分文件还原成 stage 中的样子呢?
来个实际场景,我先新建两个文件,然后把他们都加到 stage:

$ touch a.txt b.txt
$ git add .
$ git status
On branch master
Changes to be committed:
	new file:   a.txt
	new file:   b.txt

然后我又修改了 a.txt 文件:

$ echo hello world >> a.txt
$ git status
On branch master
Changes to be committed:
	new file:   a.txt
	new file:   b.txt
Changes not staged for commit:
	modified:   a.txt

现在,我后悔了,我认为不应该修改 a.txt,我想把它还原成 stage 中的空文件,怎么办?
答案是,使用 checkout 命令:

$ git checkout a.txt
Updated 1 path from the index
$ git status
On branch master
Changes to be committed:
	new file:   a.txt
	new file:   b.txt

看到了么,输出显示从 index 区(也就是 stage 区)更新了一个文件,也就是把 work dir 中 a.txt 文件还原成了 stage 中的状态(一个空文件)。

当然,如果 work dir 中被修改的文件很多,可以使用通配符全部恢复成 stage:

$ git checkout .

有一点需要指出的是,checkout 命令只会把被「修改」的文件恢复成 stage 的状态,如果 work dir 中新增了新文件,你使用 git checkout . 是不会删除新文件的。
风险等级:中风险。
理由:在 work dir 做出的「修改」会被 stage 覆盖,无法恢复。所以使用该命令你应该确定 work dir 中的修改可以抛弃。

3.将 stage 区的文件添加到 history 区

很简单,就是 git commit 相关的命令,一般我们就是这样用的:

$ git commit -m '一些描述'

再简单提一些常见场景, 比如说 commit 完之后,突然发现一些错别字需要修改,又不想为改几个错别字而新开一个 commit 到 history 区,那么就可以使用下面这个命令:

$ git commit --amend

这样就是把错别字的修改和之前的那个 commit 中的修改合并,作为一个 commit 提交到 history 区。
git commit –amend既可以对上次提交的内容进行修改,也可以修改提交说明。

4.将 history 区的文件还原到 stage 区

这个需求很常见,比如说我用了一个 git add . 一股脑把所有修改加入 stage,但是突然想起来文件 a.txt 中的代码我还没写完,不应该把它 commit 到 history 区,所以我得把它从 stage 中撤销,等后面我写完了再提交。

$ echo aaa >> a.txt; echo bbb >> b.txt;
$ git add .
$ git status
On branch master
Changes to be committed:
	modified:   a.txt
	modified:   b.txt

如何把 a.txt 从 stage 区还原出来呢?可以使用 git reset 命令:

$ git reset a.txt
$ git status
On branch master
Changes to be committed:
	modified:   b.txt
Changes not staged for commit:
	modified:   a.txt

你看,这样就可以把 a.txt 文件从 stage 区移出,这时候进行 git commit 相关的操作就不会把这个文件一起提交到 history 区了。

上面的这个命令是一个简写,实际上 reset 命令的完整写法如下:

$ git reset --mixed HEAD a.txt

其中,mixed 是一个模式(mode)参数,如果 reset 省略这个选项的话默认是 mixed 模式;HEAD 指定了一个历史提交的 hash 值;a.txt 指定了一个或者多个文件。
该命令的自然语言描述是:不改变 work dir 中的任何数据,将 stage 区域中的 a.txt 文件还原成 HEAD 指向的 commit history 中的样子。就相当于把对 a.txt 的修改从 stage 区撤销,但依然保存在 work dir 中,变为 unstage 的状态。
风险等级:低风险。
理由:不会改变 work dir 中的数据,会改变 stage 区的数据,所以应确保 stage 中被改动数据是可以抛弃的。

5.将 work dir 的修改提交到 history 区

先 git add 然后 git commit 就行了,或者一个快捷方法是使用命令 git commit -a。
风险等级:无风险。

6.将 history 区的历史提交还原到 work dir 中

这个场景,我说一个极端一点的例子:比如我从 GitHub 上 clone 了一个项目,然后乱改了一通代码,结果发现我写的代码根本跑不通,于是后悔了,干脆不改了,我想恢复成最初的模样,怎么办?
依然是使用 checkout 命令,但是和之前的使用方式有一些不同:

$ git checkout HEAD .
Updated 12 paths from d480c4f

这样,work dir 和 stage 中所有的「修改」都会被撤销,恢复成 HEAD 指向的那个 history commit。
注意,类似之前通过 stage 恢复 work dir 的 checkout 命令,这里撤销的也只是修改,新增的文件不会被撤销。
当然,只要找到任意一个 commit 的 HASH 值,checkout 命令可就以将文件恢复成任一个 history commit 中的样子:

$ git checkout 2bdf04a some_test.go
Updated 1 path from 2bdf04a

前文的用法显示 update from index
比如,我改了某个测试文件,结果发现测试跑不过了,所以就把该文件恢复到了它能跑过的那个历史版本……
风险等级:高风险。
理由:这个操作会将指定文件在 work dir 的数据恢复成指定 commit 的样子,且会删除该文件在 stage 中的数据,都无法恢复,所以应该慎重使用。

参考链接:
【1】我用四个命令概括了 GIT 的所有套路

posted @ 2022-01-09 14:51  nxf_rabbit75  阅读(61)  评论(0编辑  收藏  举报