在上一节中我们的demo版本库经历了一次提交,我们可以使用git og --stat查看一下提交日志。
[root@git demo]# git log --stat
commit 986a1bd458ef762ddb4e4622023e8b7d4044a665
Author: Gao Yue <dgy@89mc.com>
Date: Fri Aug 25 11:55:06 2017 +0800
initwelcome
welcome.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
可以看待本次提交对文件welcome.txt有一行的变更。下面仍在这个工作区中学习暂存区。
我们试一下修改过后的文件能不能直接提交
[root@git demo]# echo "welcome to beijing" >> welcome.txt
[root@git demo]# git diff //通过diff可以看到修改后的文件与版本库中文件的差异。
diff --git a/welcome.txt b/welcome.txt
index 18832d3..50886cc 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1 +1,2 @@
Hello.
+welcome to beijing
[root@git demo]# git commit -m "append new line"
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: welcome.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
从上面的输出看来好像没有提交成功,我们来查一下看看有没有提交日志
[root@git demo]# git log --pretty=oneline
986a1bd458ef762ddb4e4622023e8b7d4044a665 initwelcome //只有上一次提交的日志,说明本次没有提交成功
为什么会提交失败呢?我们来翻译一下git commit后的输出:
# On branch master //位于您当前工作的分支master上
# Changed but not updated: //下列修改还没有加入到提交任务中,不会被提交
# (use "git add <file>..." to update what will be committed) //使用git add <file>命令后改动就会加入到提交任务中
# (use "git checkout -- <file>..." to discard changes in working directory) //使用git checkout -- <file>命令,会删除不打算提交的文件
#
# modified: welcome.txt //已修改:welcome.txt
#
no changes added to commit (use "git add" and/or "git commit -a") //警告:提交任务是空的,请使用git add和/或git commit -a命令。
也就是说,需要对修改的文件执行git add命令,将修改的文件添加到“提交任务”中后才能提交。
[root@git demo]# git add welcome.txt
[root@git demo]# echo "bye-bye" >> welcome.txt
[root@git demo]# git commit -m "version 2 welcome"
[master e97f443] version 2 welcome
1 files changed, 1 insertions(+), 0 deletions(-)
[root@git demo]# git log --pretty=oneline
e97f443b2d1cee7eeca7dc2e768f599980788c3c version 2 welcome
986a1bd458ef762ddb4e4622023e8b7d4044a665 initwelcome
可以看出经过git add后的文件提交成功,日志输出中也有我们的提交记录。
理解git暂存区(stage)
上面的示例中我们用到了"提交任务"这个概念,但是暂存区才是其真正的名称,在.git目录下有一个index文件,我们做一个测试
<1>首先执行git checkout命令撤销工作区中welcome.txt文件尚未提交的修改
[root@git demo]# git checkout -- welcome.txt
[root@git demo]# git status -s
<2>通过状态输出可以看到工作区已经没有改动,查一下.git/index文件
[root@git demo]# ls --full-time .git/index //时间戳为13:36:26
-rw-r--r--. 1 root root 112 2017-08-25 13:36:26.026673742 +0800 .git/index
<3>再次执行git status命令,然后显示.git/index文件时间戳,没有变化
[root@git demo]# git status -s
[root@git demo]# ls --full-time .git/index
-rw-r--r--. 1 root root 112 2017-08-25 13:36:26.026673742 +0800 .git/index
<4>现在更改下welcome.txt的时间戳,但是不改变其内容,再执行git status命令。时间戳为13:40:26
[root@git demo]# touch welcome.txt
[root@git demo]# git status -s
[root@git demo]# ls --full-time .git/index
-rw-r--r--. 1 root root 112 2017-08-25 13:40:26.026673502 +0800 .git/index
这个实验说明当执行git status扫描工作区改动的时候,先依据.git/index文件中记录的时间戳、长度等信息判断工作区文件是否变动,如果时间戳变了,说明文件的内容可能被改变;如果没有被改变,则将该文件新的时间戳记录到index文件中。文件.git/index实际上就是一个包含文件索引的目录树,在这个目录树中,记录了文件名和文件状态信息,文件的内容并没有在这里存储,而是保存在.git/objects目录中,文件索引建立了文件和对象库中对象实体之间的对应。
图片的介绍:
<1>左侧为工作区,右侧为版本库。在版本库中标记为index的区域是暂存区,标记为master的是master分支所代表的目录树。
<2>图中此时HEAD实际是指向master分支的一个游标,所以图中的命令中出现HEAD的地方可以用master来替换。
<3>图中objects标识的区域为git对象库,实际位于.git/objects目录下。
<4>当对工作区修改文件执行git add时,暂存区的目录树将被更新,同时工作区修改的文件内容会被写入到对象库中的一个新的对象中,而该对象的ID被记录暂存区的文件索引中。
<5>当执行提交操作时,暂存区的目录树会被写到版本库中,master分支会做相应的更新,就是master最新指向的目录树就是提交时原暂存区的目录树
<6>当执行git reset HEAD时,暂存区目录树会被重写,会被master分支指向的目录树所替换,但是不影响工作区。
<7>当执行git rm --cached <file>时,会直接从暂存区删除文件,工作区不做改变。
<8>当执行git checkout .或git checkout -- <file>时,会用暂存区全部的文件或指定的文件替换工作区的文件。这个操作会清除工作区中未被添加到暂存区的改动。
<9>当执行git checkout HEAD . 或git checkout HEAD <file>时,会用HEAD指向的master分支中的全部或部分文件替换暂存区和工作区的文件。