?
This document uses PHP Chinese website manual Release
gittutorial-2 - Git 的教程介紹:第二部分
git *
閱讀本教程之前,您應該通過 gittutorial [7] 。
本教程的目標是介紹 Git 體系結(jié)構(gòu)的兩個基本部分 - 對象數(shù)據(jù)庫和索引文件,并向讀者提供理解其余 Git 文檔所需的一切。
讓我們開始一個新項目并創(chuàng)建少量歷史記錄:
$ mkdir test-project $ cd test-project $ git init Initialized empty Git repository in .git/$ echo 'hello world' > file.txt $ git add .$ git commit -a -m "initial commit"[master (root-commit) 54196cc] initial commit 1 file changed, 1 insertion(+) create mode 100644 file.txt $ echo 'hello world!' >file.txt $ git commit -a -m "add emphasis"[master c4d59f3] add emphasis 1 file changed, 1 insertion(+), 1 deletion(-)
Git 響應提交的十六進制數(shù)字是什么?
我們在教程的第一部分看到了提交這樣的名字。事實證明,Git 歷史記錄中的每個對象都以40位十六進制名稱存儲。該名稱是對象內(nèi)容的 SHA-1 散列; 除此之外,這確保了 Git 永遠不會存儲兩次相同的數(shù)據(jù)(因為相同的數(shù)據(jù)具有相同的 SHA-1 名稱),并且 Git 對象的內(nèi)容永遠不會改變(因為這也會改變對象的名稱)。這里的7個字符的十六進制字符串就是這樣的40個字符長的字符串的縮寫。縮寫可以用于任何可以使用40個字符串的地方,只要它們是明確的即可。
預計在上面的例子中創(chuàng)建的提交對象的內(nèi)容會生成與上面顯示的不同的 SHA-1 哈希值,因為提交對象會記錄創(chuàng)建時間和執(zhí)行提交人員的姓名。
我們可以用cat-file
命令向 Git 詢問這個特定的對象。不要復制這個例子中的40個十六進制數(shù)字,而是使用你自己版本的數(shù)字。請注意,您可以將其縮短為只有幾個字符,以免鍵入所有40個十六進制數(shù)字:
$ git cat-file -t 54196cc2 commit $ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500initial commit
樹可以引用一個或多個 “blob” 對象,每個對象都對應一個文件。另外,樹還可以引用其他樹對象,從而創(chuàng)建目錄層次結(jié)構(gòu)。您可以使用 ls-tree 檢查任何樹的內(nèi)容(請記住,SHA-1 的足夠長的初始部分也可以工作):
$ git ls-tree 92b8b694100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt
因此我們看到這棵樹里有一個文件。SHA-1 散列是對該文件數(shù)據(jù)的引用:
$ git cat-file -t 3b18e512 blob
“blob” 只是文件數(shù)據(jù),我們也可以用 cat-file 來檢查:
$ git cat-file blob 3b18e512 hello world
請注意,這是舊的文件數(shù)據(jù); 所以 Git 在對初始樹的響應中命名的對象是一棵樹,其中包含第一次提交記錄的目錄狀態(tài)的快照。
所有這些對象都存儲在 Git 目錄下的 SHA-1 名稱下:
$ find .git/objects/.git/objects/.git/objects/pack.git/objects/info.git/objects/3b.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad.git/objects/92.git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe.git/objects/54.git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7.git/objects/a0.git/objects/a0/423896973644771497bdc03eb99d5281615b51.git/objects/d0.git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59.git/objects/c4.git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241
而這些文件的內(nèi)容只是壓縮數(shù)據(jù)加上一個標識它們的長度和類型的頭文件。該類型是 blob ,樹,提交或標記。
最簡單的提交是 HEAD 提交,我們可以從 .git / HEAD 找到:
$ cat .git/HEAD ref: refs/heads/master
正如你所看到的,這告訴我們我們當前正在使用哪個分支,并且它通過命名 .git 目錄下的一個文件告訴我們這個文件,它本身包含引用一個提交對象的 SHA-1 名稱,我們可以用它來檢查貓文件:
$ cat .git/refs/heads/master c4d59f390b9cfd4318117afde11d601c1085f241 $ git cat-file -t c4d59f39 commit $ git cat-file commit c4d59f39 tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59 parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7 author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500add emphasis
這里的“樹”對象指的是樹的新狀態(tài):
$ git ls-tree d0492b36100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt $ git cat-file blob a0423896 hello world!
而“父”對象引用了之前的提交:
$ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500initial commit
樹對象是我們首先檢查的樹,而這個提交是不尋常的,因為它缺少任何父對象。
大多數(shù)提交只有一個父,但是承諾有多個父也很常見。在這種情況下,提交表示合并,并且父引用指向合并分支的 HEAD 。
除了斑點,樹和提交之外,唯一剩下的對象就是一個“tag”,我們不在這里討論; 有關(guān)詳細信息,請參閱 git-tag [1] 。
所以現(xiàn)在我們知道 Git 如何使用對象數(shù)據(jù)庫來表示一個項目的歷史記錄:
“commit”對象指的是表示歷史中特定點上的目錄樹快照的“樹”對象,并參考“父”提交來顯示它們?nèi)绾芜B接到項目歷史中。
“樹”對象表示單個目錄的狀態(tài),將目錄名稱與包含文件數(shù)據(jù)的 “blob” 對象以及包含子目錄信息的“樹”對象相關(guān)聯(lián)。
“blob”對象包含沒有任何其他結(jié)構(gòu)的文件數(shù)據(jù)。
在每個分支頭部提交對象的引用存儲在 .git / refs / heads / 下的文件中。
當前分支的名稱存儲在 .git / HEAD 中。
請注意,順便提一下,很多命令都以樹為參數(shù)。但是,正如我們上面所看到的,樹可以以許多不同的方式引用 - 通過樹的 SHA-1 名稱,引用樹的提交的名稱,引用其頭部的分支的名稱到那棵樹等等 - 而且大多數(shù)這樣的命令可以接受任何這些名字。
在命令提要中,有時用 “tree-ish” 這個詞來表示這樣一個參數(shù)。
我們用來創(chuàng)建提交的主要工具是git-commit -a
創(chuàng)建一個提交,包括您對工作樹所做的每個更改。但是,如果您只想對某些文件進行更改,該怎么辦?或者只對某些文件進行某些更改?
如果我們看一下在封面下創(chuàng)建提交的方式,我們會看到創(chuàng)建提交的方式更加靈活。
繼續(xù)我們的測試項目,讓我們再次修改 file.txt :
$ echo "hello world, again" >>file.txt
但是這次不是立即做出提交,而是讓我們采取中間步驟,并沿途詢問差異來跟蹤發(fā)生的事情:
$ git diff--- a/file.txt+++ b/file.txt @@ -1 +1,2 @@ hello world!+hello world, again $ git add file.txt $ git diff
最后的差異是空的,但沒有新的提交已經(jīng)完成,并且 head 仍然不包含新行:
$ git diff HEAD diff --git a/file.txt b/file.txt index a042389..513feba 100644--- a/file.txt+++ b/file.txt @@ -1 +1,2 @@ hello world!+hello world, again
所以git diff
比較頭部以外的東西。它比較的東西實際上是索引文件,它以二進制格式存儲在 .git / index 中,但其內(nèi)容可以用 ls-files 檢查:
$ git ls-files --stage100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt $ git cat-file -t 513feba2 blob $ git cat-file blob 513feba2 hello world!hello world, again
所以我們git add
做的是存儲一個新的 blob ,然后在索引文件中加入一個引用。如果我們再次修改文件,我們會看到新的修改反映在git diff
輸出中:
$ echo 'again?' >>file.txt $ git diff index 513feba..ba3da7b 100644--- a/file.txt+++ b/file.txt @@ -1,2 +1,3 @@ hello world! hello world, again+again?
使用正確的參數(shù),git diff
還可以顯示工作目錄和上次提交之間或索引和上次提交之間的區(qū)別:
$ git diff HEAD diff --git a/file.txt b/file.txt index a042389..ba3da7b 100644--- a/file.txt+++ b/file.txt @@ -1 +1,3 @@ hello world!+hello world, again+again?$ git diff --cached diff --git a/file.txt b/file.txt index a042389..513feba 100644--- a/file.txt+++ b/file.txt @@ -1 +1,2 @@ hello world!+hello world, again
在任何時候,我們都可以使用git commit
(不帶 “-a” 選項)創(chuàng)建一個新的提交,并驗證提交的狀態(tài)只包含索引文件中存儲的更改,而不是僅存在于工作樹中的更改。
$ git commit -m "repeat"$ git diff HEAD diff --git a/file.txt b/file.txt index 513feba..ba3da7b 100644--- a/file.txt+++ b/file.txt @@ -1,2 +1,3 @@ hello world! hello world, again+again?
所以默認情況下git commit
使用索引來創(chuàng)建提交,而不是工作樹; 提交的 “-a” 選項告訴它首先用工作樹中的所有更改更新索引。
最后,值得關(guān)注git add
索引文件的效果:
$ echo "goodbye, world" >closing.txt $ git add closing.txt
該功能的作用git add
是將一個條目添加到索引文件中:
$ git ls-files --stage100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing.txt100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
而且,正如您可以用 cat-file 看到的那樣,這個新條目引用了該文件的當前內(nèi)容:
$ git cat-file blob 8b9743b2 goodbye, world
"status" 命令是快速總結(jié)情況的有用方法:
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: closing.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: file.txt
由于 closing.txt 的當前狀態(tài)被緩存在索引文件中,因此它被列為“要提交的更改”。由于 file.txt 在工作目錄中的變化未反映在索引中,因此它被標記為“已更改但未更新”。此時,運行 “git commit” 會創(chuàng)建一個提交 clos.txt(及其新內(nèi)容)的提交,但不會修改 file.txt 。
此外,請注意,bare 會git diff
顯示對 file.txt 的更改,但不會增加 closing.txt ,因為索引文件中 closing.txt 的版本與工作目錄中的版本相同。
除了作為新提交的暫存區(qū)域之外,還在檢出分支時從對象數(shù)據(jù)庫填充索引文件,并且該文件用于保存涉及合并操作的樹。有關(guān)詳細信息,請參閱 gitcore-tutorial [7] 和相關(guān)手冊頁。
此時,您應該知道讀取任何 git 命令的手冊頁所需的一切; 在 giteveryday [7] 中提到的命令是一個很好的開始。你應該能夠在 gitglossary 中找到任何未知的術(shù)語[7]。
Git 用戶手冊提供了更全面的 Git 介紹。
gitcvs-migration [7] 解釋了如何將 CVS 存儲庫導入到 Git 中,并展示了如何以類似 CVS 的方式使用 Git 。
有關(guān) Git 使用的一些有趣示例,請參閱 howtos。
對于 Git 開發(fā)人員,gitcore-tutorial [7] 詳細介紹了涉及創(chuàng)建新提交的較低級 Git 機制。