前篇

之前提交过demo,如何查看日志呢

1
2
3
4
5
6
7
8
9
10
$ cd /git/test/demo
$ git log --stat
commit ac01ff39a78fa388e1e628754fd55a76feb358d2 (HEAD -> master)
Author: smxknife <2323937771@qq.com>
Date: Tue Mar 27 23:38:05 2018 +0800

init hellogit file

hellogit | 1 +
1 file changed, 1 insertion(+)

可以看到修改记录

diff

现在对文件进行修改

$ echo "new change" >> hellogit

使用diff命令进行比较

1
2
3
4
5
6
7
8
$ git diff
diff --git a/hellogit b/hellogit
index d974466..c49fee4 100644
--- a/hellogit
+++ b/hellogit
@@ -1 +1,2 @@
Hello Git.
+new change

从上面来看确实修改了,也存在差异,那么久提交吧

1
2
3
4
5
6
$ git commit -m "modify hellofile, add new change"
On branch master
Changes not staged for commit:
modified: hellogit

no changes added to commit

从输出结果上看好像有点问题,貌似没提交成功,通过日志来看

1
2
$ git log --pretty=oneline
ac01ff39a78fa388e1e628754fd55a76feb358d2 (HEAD -> master) init hellogit file

日志里也只有一行输出,查看文件状态

$ git status或者$ git status -s(简略输出信息)
输出如下:

1
2
$ git status -s
M hellogit

无论怎么看都是提交失败,什么原因????这就涉及到了暂存区的概念

暂存区(stage)

git规定修改文件之后要先执行add操作,虽然感觉很别扭,但是无奈只能这么做

$ git add hellogit

再用git diff会发现没有任何输出,再执行git status -s

1
2
$ git status -s
M hellogit

乍一看没什么变化,但是请与上面对比来看,是不是M的位置发生了变化,在add操作之前M的位置在第二列,add之后M的位置为第一列

第一列M代表的含义是:仓库中的文件与暂存区中的文件相比有改动
第二列M代表的含义是:工作区中的当前文件与暂存区的文件相比有改动

那么执行add之前,M位于第二列,说明比较的是工作区与暂存区,而add之后比较的是仓库与暂存区

从这可以看出add的目的就是将工作区的文件提交到暂存区

那么再修改一下文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo "third change." >> hellogit 

$ git status -s
MM hellogit

$ git diff
diff --git a/hellogit b/hellogit
index c49fee4..990080f 100644
--- a/hellogit
+++ b/hellogit
@@ -1,2 +1,3 @@
Hello Git.
new change
+third change.

从上面的结果来看,hellogit的status存在两个M,说明工作区相比暂存区有更改,暂存区相比仓库有更改。而此时git diff命令有输出结果了,说明git diff 默认是工作区与暂存区进行比较

从这个结果来看,文件在Git中会存在三种状态,仓库中的状态、暂存区中的状态、工作区中的当前状态

将工作区和HEAD(当前工作分支)相比

1
2
3
4
5
6
7
8
9
$ git diff HEAD
diff --git a/hellogit b/hellogit
index d974466..990080f 100644
--- a/hellogit
+++ b/hellogit
@@ -1 +1,3 @@
Hello Git.
+new change
+third change.

将暂存区和仓库相比 (–cached或–staged)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git diff --cached
diff --git a/hellogit b/hellogit
index d974466..c49fee4 100644
--- a/hellogit
+++ b/hellogit
@@ -1 +1,2 @@
Hello Git.
+new change

$ git diff --staged
diff --git a/hellogit b/hellogit
index d974466..c49fee4 100644
--- a/hellogit
+++ b/hellogit
@@ -1 +1,2 @@
Hello Git.
+new change

好了,现在进行commit操作,那么将会提交的是哪个版本呢?

1
2
3
$ git commit -m "which version checked in?"
[master 0789463] which version checked in?
1 file changed, 1 insertion(+)

通过上面来看只有一行插入,所以两个变化只提交了一个

再通过一系列命令来查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ git status -s
M hellogit

$ git diff
diff --git a/hellogit b/hellogit
index c49fee4..990080f 100644
--- a/hellogit
+++ b/hellogit
@@ -1,2 +1,3 @@
Hello Git.
new change
+third change.

$ git diff HEAD
diff --git a/hellogit b/hellogit
index c49fee4..990080f 100644
--- a/hellogit
+++ b/hellogit
@@ -1,2 +1,3 @@
Hello Git.
new change
+third change.

$ git diff --staged
# 没有任何输出

通过上面一系列的输出来看,这次commit只提交了暂存区的文件修改,工作区的依然没有变化,所以,本地工作区每次修改文件后都需要先add到暂存区,然后再commit到仓库中。那么将所有的更改先提交到仓库吧

前面提到过.git/index文件,其实这个index就是暂存区文件,当执行git status扫描工作区改动的时候,先根据.git/index文件记录的时间戳、长度等信息判断工作区文件是否改变,如果工作区文件时间戳改变了,说明文件内容可能被改变了,需要打开文件读取内容,与原始文件进行比较,判断文件是否被改变。如果没有变化就将新的时间戳记录到.git/index文件中。通过时间戳和文件长度比较比文件内容比较快的多,这正是git高效的原因之一

通过下面的一张图来形象的理解一下工作区、暂存区、仓库(版本库)的原理

index其实就是一个索引目录树,记录了文件名和文件状态信息(时间戳和长度),真正的文件内容是存储在.git/objects目录中

$ git reset HEAD重写暂存区的目录树,此时会被master分支指向的目录树替换,但是不影响工作区

$ git rm --staged <fileName>会直接从暂存区删除文件,工作区不受影响

$ git checkout$ git checkout -- <fileName> 这是两个非常危险的操作,前者会将暂存区所有的文件替换工作区的文件,后者是替换指定的文件。这两个操作都会导致工作区修改的且未添加的修改丢失,慎重!!!

$ git checkout HEAD$ git checkout HEAD <fileName>这个与上面的类似,但是更加危险,会用仓库里面文件替换暂存区和工作区两个地方

一些命令

查看HEAD指向的目录树

git ls-tree -l HEAD

清理工作区

突然发现工作区太乱了,没有有用的东西需要清理

1
2
$ git clean -fd # 清除工作区中没有加入仓库的文件和目录
$ git checkout . # 使用暂存区进行刷新

查看暂存区的目录树

git ls-files -s

不要使用git commit -a

Git提供了一个简化命令,git commit -a 减少了add的步骤,可以直接将修改的文件进行commit,但是未被版本库跟踪的文件时不能提交的。

这样做确实简化了操作步骤,但是就会丢掉Git暂存区的最大好处:对提交的内容进行控制的能力