《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命令,發現工做區中還沒有提交的改動都不見了。

相關文章
相關標籤/搜索