《Git权威指南》读书笔记 第五章 Git暂存区

5.1 修改不能直接提交git

首先修改welcome.txt文件,在这个文件后面追加一行:svn

echo "Nice to meet you." >> welcome.txt

使用git diff命令查看修改后的文件与暂存区(并非版本库,后面会有相关讨论)中的文件的差别:3d

diff --git a/welcome.txt b/welcome.txt
index a8f9fd8..b0e5c6e 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1 +1,2 @@
 Hello
+Nice to meet you.

差别的输出格式:指针

  • 以---开头的文件表示源文件;
  • 以+++开头的文件表示目标文件;
  • @@ 中间的数字表示行号和行数,-1表示源文件第一行开始共一行,+1,2表示目标文件从第一行开始共2行;
  • 文件内容中以空格开头的表示源文件和目标文件共同拥有的行,-开头的行表示只在源文件中出现的行,+开头的行表示只在目标文件出现的行。

此时使用git commit命令并不会提交修改,反而会报错:code

git commit -m "Append a nice line."

On branch master
Changes not staged for commit:
        modified:   welcome.txt

no changes added to commit

使用git status查看文件状态,加上-s参数显示精简格式的状态输出:对象

git status
On branch master
Changes not staged for commit:
  (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"
git status -s
 M welcome.txt

welcome.txt文件处于修改状态。递归

根据git status输出的提示,须要对修改的文件执行git add命令,将修改的文件添加到“提交任务”中,而后才能提交。此处与svn差异很大,在svn中执行add操做是向版本库中添加新文件,修改的文件在下次提交时会直接提交。索引

按照提示将修改的文件添加到提交任务中:it

git add welcome.txt

在执行完添加操做,而没有执行提交操做时,查看一下Git工做区发生了什么变化:io

一、执行git diff没有输出,说明是本地文件与暂存区的比较;

二、执行git diff HEAD(当前版本库的头指针),会有差别输出,说明此时比较的是本地文件和版本库的最新版本;

三、执行git status,输出与添加前不同了:

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   welcome.txt

四、执行git status -s,以简洁方式显示状态:

git status -s
M  welcome.txt

乍一看貌似与添加前的输出同样,其实它们有细微的差异:

  • 虽然都是M(Modified)标识,可是位置不同。在执行git add命令以前,M位于第二列(第一列是一个空格),在执行git add命令以后,M位于第一列(第二列是空白)。
  • 位于第一列的M的含义是:版本库中的文件与暂存区的文件相比有改动;
  • 位于第二列的M的含义是:工做区当前的文件与处于赞春去的文件相比有改动。

为了加深理解,暂不提交以前的添加任务,而继续修改文件:

echo "Bye-Bye." >> welcome.txt

查看一下状态:

git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   welcome.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   welcome.txt

这里同时出现了前文讨论的两种状态。再查看精简的状态:

git status -s
MM welcome.txt

同时出现了两个M:不但版本库中最新提交的文件与处于暂存区中的文件相比有改动,并且工做区与暂存区中的文件相比也有改动。

即如今有三个不一样版本的welcome.txt:一个在工做区,一个在暂存区,一个是版本库中最新版本的welcome.txt。

使用不一样参数调用git diff命令能够看到不一样状态下welcome.txt文件的差别:

  • 不带任何参数的git diff比较工做区和暂存区的差别:
  • git diff
    diff --git a/welcome.txt b/welcome.txt
    index b0e5c6e..45cf837 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1,2 +1,3 @@
     Hello
     Nice to meet you.
    +Bye-Bye.
  • git diff HEAD比较工做区和版本库最新版本的差别:
  • git diff HEAD
    diff --git a/welcome.txt b/welcome.txt
    index a8f9fd8..45cf837 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1 +1,3 @@
     Hello
    +Nice to meet you.
    +Bye-Bye.
  • 带--cached 或 --staged参数查看暂存区和版本库之间的差别:
  • git diff --staged
    diff --git a/welcome.txt b/welcome.txt
    index a8f9fd8..b0e5c6e 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1 +1,2 @@
     Hello
    +Nice to meet you.

执行提交操做:

git commit -m "which version checked in?"
[master 326f237] which version checked in?
 1 file changed, 1 insertion(+)

提交成功,使用git status -s查看状态:

git status -s
 M welcome.txt

文件名前少了第一个M,只剩下第二个M,说明暂存区的任务被提交到了版本库,如今只剩下工做区和暂存区的差别。

5.2 理解Git暂存区

在版本库.git目录下有一个index文件,看一下这个文件:

一、撤销工做区中welcome.txt文件还没有提交的修改(5.1中添加的Bye-Bye那一行)并查看工做区状态:

git checkout -- welcome.txt
git status -s

git status -s无输出,说明未提交的修改已经被撤销了。

二、查看.git/index的时间戳:

ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:52:20.640437700 +0800 .git/index

时间戳是刚刚运行git status -s的时间。

三、再次查看工做区状态并查看.git/index的时间戳:

git status -s
ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:52:20.640437700 +0800 .git/index

时间戳没有变化。

四、更改welcome.txt文件的时间戳,但并不改变它的内容(可使用touch命令,它用来修改文件时间戳,或者新建一个不存在的文件)。而后查看工做区状态并查看.git/index的时间戳:

touch welcome.txt
git status -s
ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:58:10.512449200 +0800 .git/index

发现git status -s依然无输出,说明工做区没有新的修改;可是.git/index时间戳改变了。

以上的实验说明当执行git status命令(或者git diff命令)扫描工做区改动的时候,先依据.git/index文件中记录的时间戳、长度等信息判断工做区文件是否改变。若是工做区文件的时间戳改变了,说明文件的内容可能被改变了,须要打开文件、读取文件内容,与更改前的原始文件相比较,判断文件内容是否被更改。若是文件内容没有改变,则将该文件新的时间戳记录到.git/index文件中。由于若是要判断文件是否更改,使用时间戳、文件长度等信息进行比较要比经过文件北荣比较快得多,因此Git这样的实现方式可让工做区状态扫描更快速的执行。

文件.git/index是包含文件索引的目录树,记录了文件名和文件的状态信息,文件的内容没有存储在其中,而是保存在Git对象库.git/objects目录中,文件索引创建了文件和对象库的对应关系。

下图展现了工做区、版本库中的暂存区和版本库之间的关系:

  • 图中左侧为工做区,右侧为版本。在版本库中标记为index的区域是暂存区,标记为master的是master分支所表明的目录树;
  • HEAD指向master分支,因此图中所示命令中出现HEAD地方能够用master来替换;
  • 图中的objects表示的区域为Git的对象库,位于.gti/objects目录下;
  • 当对工做区修改(或新增)的文件执行git add命令时,暂存区的目录树将被更新,同时工做区修改(或新增)的文件内容会被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中;
  • 当执行提交操做时,暂存区的目录树会写到版本库中,master指向新写入的目录树(内容与提交的暂存区的目录树一致);
  • 当执行git reset HEAD时,暂存区的目录树会被重写,会被master分支指向的目录树的内容所替换,可是工做区不受影响;
  • 当执行git rm --cahced <file> 命令时,会直接从暂存区删除文件,工做区不会改变;
  • 当执行git checkout . 或 git checkout -- <file>命令时,会用暂存区所有的文件或指定的文件替换工做区的文件,即这个命令会清除工做区中未添加到暂存区的改动;
  • 当执行git checkout HEAD . 或 git checkout HEAD <file> 命令时,会用HEAD指向的master分支中的所有或部分文件替换暂存区和工做区的文件,即它会清除工做区中未添加的改动还会清除暂存区中未提交的改动。

5.3 浏览暂存区和版本库的目录树、git diff

一、浏览暂存区和版本库的目录树

对于HEAD指向的目录树,可使用git ls-tree来查看,-l参数可现实文件的大小:

git ls-tree -l HEAD
100644 blob b0e5c6e24bc84d489773b2fda5accf005bc912f1      25    welcome.txt

输出中的信息从左到右:

  • 第一个字段是文件的读写属性(rw-r--r--);
  • 第二个字段说明是Git对象库中的一个blob对象(文件);
  • 第三个字段是该文件在对象库中的ID(一个40位的SHA1哈希值);
  • 第四个字段是文件大小(单位是字节);
  • 第五个字段是文件名。

下面的命令清除当前工做区中没有加入版本库的文件和目录:

git clean -fd

下面的命令用暂存区内容刷新工做区:

git checkout .

对工做区作一些修改:

$ echo "Bye-Bye." >> welcome.txt

$ mkdir -p a/b/c

$ echo "Hello." > a/b/c/hello.txt

$ git add .

x250@x250-PC MINGW64 ~/demo (master)
$ echo "Bye-Bye." >> a/b/c/hello.txt

$ git status -s
AM a/b/c/hello.txt
M  welcome.txt

查看工做区文件的大小:

$ find . -path ./.git -prune -o -type f -printf "%-20p\t%s\n"
./a/b/c/hello.txt       16
./welcome.txt           36

显示暂存区的目录树:

git ls-files -s
100644 18832d35117ef2f013c4009f5b2128dfaeff354f 0       a/b/c/hello.txt
100644 45cf8376b221e28cb9c5afb382cfe15ceb3dc520 0       welcome.txt

-s命令要去显示暂存区中Object的对象名。输出中的0不是文件大小而是暂存区编号。若是想针对暂存区的目录树使用git ls-tree命令,须要先将暂存区的目录树写入Git对象库(git write-tree):

$ git write-tree
1343285bb5205fe9eed661efebcc9d65bf0cea7e

$ git ls-tree -l 1343285
040000 tree 53583ee687fbb2e913d18d508aefd512465b2092       -    a
100644 blob 45cf8376b221e28cb9c5afb382cfe15ceb3dc520      34    welcome.txt

git write-tree的输出是写入Git对象库中的TreeID,这个ID将做为下一条命令的参数。

在git ls-tree命令中,没有把40位的ID写全,而是使用了前几位,实际上只要不与其余对象的ID冲突,就可使用缩写ID。

git ls-tree输出的第一条显示目录a是一个tree对象。

若是想要递归显示目录内容,则使用-r参数。使用参数-t能够把递归中遇到的每棵树都显示出来,而不是只显示最终文件。

二、git diff

经过调用git diff并添加不一样参数,能够对工做区、暂存区和HEAD中的内容进行两两比较。以下图所致(1)工做区和暂存区的比较:

$ git diff
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
index 18832d3..e8577ea 100644
--- a/a/b/c/hello.txt
+++ b/a/b/c/hello.txt
@@ -1 +1,2 @@
 Hello.
+Bye-Bye.

(2)暂存区和HEAD比较

git diff --cached
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
new file mode 100644
index 0000000..18832d3
--- /dev/null
+++ b/a/b/c/hello.txt
@@ -0,0 +1 @@
+Hello.
diff --git a/welcome.txt b/welcome.txt
index b0e5c6e..45cf837 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
 Hello
 Nice to meet you.
+Bye-Bye.

(3)工做区和HEAD比较

git diff HEAD
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
new file mode 100644
index 0000000..e8577ea
--- /dev/null
+++ b/a/b/c/hello.txt
@@ -0,0 +1,2 @@
+Hello.
+Bye-Bye.
diff --git a/welcome.txt b/welcome.txt
index b0e5c6e..45cf837 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
 Hello
 Nice to meet you.
+Bye-Bye.

5.4 不要使用git commit -a

Git的提交命令(git commit)能够带上-a参数,对本地全部变动的文件执行提交操做,包括对本地修改的文件和删除的文件,但不包括未被版本库跟踪的文件。

这个命令的确能够简化一些操做,减小用git add命令表示变动文件的步骤,可是若是习惯了使用这种偷懒的提交命令,机会丢掉Git暂存区最大的好处:对提交内容进行控制的能力。做者推荐不要使用git commit -a。

5.5 保存当前的工做进度

git stash
Saved working directory and index state WIP on master: 326f237 which version checked in?
HEAD is now at 326f237 which version checked in?

$ git status
On branch master
nothing to commit, working directory clean

git stash命令备份当前的工做区的内容,从最近的一次提交中读取相关内容,让工做区保证和上次提交的内容一致。同时,将当前的工做区内容保存到Git栈中。

运行完git stash后使用git status命令,发现工做区中还没有提交的改动都不见了。

相关文章
相关标签/搜索