看日记学git

《看日记学git》之十二

《看日记学git》系列

《看日记学git》之序 《看日记学git》之一

《看日记学git》之二 《看日记学git》之三

《看日记学git》之四 《看日记学git》之五

《看日记学git》之六 《看日记学git》之七

《看日记学git》之八 《看日记学git》之九(总结)

《看日记学git》之十 《看日记学git》之十一

===

这次重点讲解索引文件(index file)的作用。

我们在提交工作时,使用最多的命令就是git commit -a了,但是这个将提交你所做的所有工作。其实,如果你了解commit的工作机制,你会知道我们可以自定义提交哪些部分到哪些工作树中,其实自由度很大的。

1

还记得之前我们建立的test-project工作目录么。我们继续在这个目录下演示讲解。

[rocrocket@wupengchong test-project]$ echo “hello world,again”>>file.txt

这次,我们不急着执行commit命令,而是先用git diff看看差别情况:

[rocrocket@wupengchong test-project]$ git diff
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again
好了,我们可以看到git报告了我们刚才所做的修改。下面我们来add一下,然后再git diff,看看diff有什么变化呢:

[rocrocket@wupengchong test-project]$ git add file.txt
[rocrocket@wupengchong test-project]$ git diff
[rocrocket@wupengchong test-project]$
大家可以看到在add之后的git diff的输出竟然为空了,但是此时我们尚未执行commit阿。如果这个时候你执行git diff HEAD,你仍然会看到修改报告:

[rocrocket@wupengchong test-project]$ git diff HEAD
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again

这就说明了一个问题:git diff不是在和HEAD比,而是和另外一个“神秘”内容在比,而这个“神秘”内容就是“索引文件”!

索引文件(index file)就是.git/index文件,它是二进制形式的文件。我们可以用ls-files来检查它的内容。

[rocrocket@wupengchong test-project]$ git ls-files –stage  //一定要记住,此命令是用于查看index file的!!
100644 a55961026a22bdd4e938dcc90a4a83823a81f776 0       file.txt
[rocrocket@wupengchong test-project]$ git cat-file -t a5596
blob
[rocrocket@wupengchong test-project]$ git cat-file blob a5596
Hi,rocrocket!
hello world,again

很明显,我们可以看到其内容已经是改进后的代码了,怪不得git-diff会输出空呢!

我们的结论就是git add的作用就是创建一个blob文件来记录最新的修改代码,并且在index file里添加一个到此blob的链接。

2

如果在git-diff后面加上参数HEAD,则git-diff会显示当前工作目录和最近一次提交之间的代码区别。

[rocrocket@wupengchong test-project]$ echo ‘again?’>>file.txt
[rocrocket@wupengchong test-project]$ git diff HEAD
diff –git a/file.txt b/file.txt
index 60be00d..dfb67dc 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,3 @@
Hi,rocrocket!
+hello world,again
+again?

如果使用参数–cached,则会比较index file和最近一次提交之间的代码区别。

[rocrocket@wupengchong test-project]$ git diff –cached
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again

按我的认识,更清楚且通俗的解释就是:git维护的代码分成三部分,“当前工作目录”<->“index file”<->git仓库。git commit会将index file中的改变写到git仓库;git add会将“当前工作目录”的改变写到“index file”;“commit -a”则会直接将“当前工作目录”的改动同时写到“index file”和“git仓库”。

而git diff的使用稍微有些复杂,大家可以看看Lee.MaRS对于这个命令非常精彩的分析(蓝色字部分):(在此非常感谢Lee.MaRS)

将 Current working directory 记为 (1)
将 Index file 记为 (2)
将 Git repository 记为 (3)

他们之间的提交层次关系是 (1) -> (2) -> (3)
git add完成的是(1) -> (2)
git commit完成的是(2) -> (3)
git commit -a两者的直接结合

从时间上看,可以认为(1)是最新的代码,(2)比较旧,(3)更旧
按时间排序就是 (1) <- (2) <- (3)

git diff得到的是从(2)到(1)的变化
git diff –cached得到的是从(3)到(2)的变化
git diff HEAD得到的是从(3)到(1)的变化

3

status命令会显示当前状态的一个简单总结:

[rocrocket@wupengchong test-project]$ git status
# On branch master
# Changes to be committed:
#   (use “git reset HEAD <file>…” to unstage)
#
#    modified:   file.txt
#
# Changed but not updated:
#   (use “git add <file>…” to update what will be committed)
#
#    modified:   file.txt
#
上面两行黑体字中的Changes to be committed表示在index和commit的区别状况。而Changed but not updated表示当前目录和index的区别状况。

ps:下一步我们将学习Everyday Git.

===
如果你对git感兴趣,请继续阅读:

《看日记学git》之十三

《看日记学git》之十四

《看日记学git》之十五

over~

3条评论

  1. 看评论然后想了想,才明白,为什么git add之后git diff HEAD会有输出…(其实和评论无关,只是给了一个提醒,是自己没想清楚)

  2. To: Lee.MaRS
    非常感谢你的建议。我会立刻修改本篇文章中的错误。也会在以后的写作中尽量避免此类错误的发生。
    :)
    其实我在后续的git文章中领悟到了这三层关系,不过在写这篇文章时还没有你理解的这么透彻。
    再次感谢你的建议!

  3. 文中这一段有些小错误:
    [quote]按我的认识,更清楚且通俗的解释就是:git维护的代码分成三部分,“当前工作目录”“index file”git仓库。git commit会将index file中的改变写到git仓库;git add会将“当前工作目录”的改变写到“index file”;“commit -a”则会直接将“当前工作目录”的改动同时写到“index file”和“git仓库”。而git diff总会拿git仓库来作为比较对象之一。如果git diff的参数是HEAD,则另一个比较对象就确定为“当前工作目录”;如果参数是–cached,则另一个比较对象就被确定为“index file”。[/quote]

    事实上,“git diff总拿git repository作为比较对象”这一点不正确。
    将 Current working directory 记为 (1)
    将 Index file 记为 (2)
    将 Git repository 记为 (3)

    他们之间的提交层次关系是 (1) -> (2) -> (3)
    git add完成的是(1) -> (2)
    git commit完成的是(2) -> (3)
    git commit -a两者的直接结合

    从时间上看,可以认为(1)是最新的代码,(2)比较旧,(3)更旧
    按时间排序就是 (1) <- (2) <- (3)

    git diff得到的是从(2)到(1)的变化
    git diff –cached得到的是从(3)到(2)的变化
    git diff HEAD得到的是从(3)到(1)的变化

    仔细阅读man git-diff就会发现,其实里面已经写清楚了。:-)

发表您的评论

请您放心,您的信息会被严格保密。必填项已标识 *