金牌译作 Git 初级教程:第二部分

923个读者 翻译: et  05/03/2008 原文 引用 双语对照及眉批 字体大小

简介

Git  初级教程的第二部分;  更深入的介绍了Git的基本概念;

    阅读本教程之前最好先浏览一遍 “Git 初级教程(第一部分)”。
   
    本教程主要介绍了 Git 的两个基本结构:“对象数据库”(object database)和“索引文件”(index file);提供给读者能够阅读Git其它文档所需的必备知识;

Git 对象数据库(object database)

    我们先来创建一个新的示例工程,然后再创建并修改一个文件作为Git历史纪录:
$ 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"
Created initial commit 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
 create mode 100644 file.txt
$ echo 'hello world!' >file.txt
$ git commit -a -m "add emphasis"
Created commit c4d59f390b9cfd4318117afde11d601c1085f241
   
    所有的提交(commit)成功后,git都会显示一个16进制数字(长度为40),这些数字代表什么呢?
   
    所有被储存在Git历史纪录中的对象都由一个长度为40的16进制数来命名。这个名字其实就是Git对象内容的SHA1值;这就确保了Git不会把相同内容的数据保存两次(既然相同内容的数据有相同的SHA1值);此外,一个Git对象的内容将永远不会变动(因为如果对象的内容变动了,对象名字也要变动)。
   
    如果你按照上面的例子创建自己的示例工程和提交自己的对象,你多半会看到Git显示一个和上面例子中不同的SHA1值。这是因为Git的提交对象(commit object)纪录了对象的创建时间和对象提交人的名字(译注,所以虽然提交的是相同内容的文件,但是是由不同的人在不同的时间的提交,提交对象的SHA1值也不一样)。
 
    我们可以用cat-file命令来让向Git查询某个对象(用你自己例子中的40位长的16进制数字,而不要直接拷贝上面示例的数字)。注意:不一定要用完整的40个数字,你可以用40个数字中的前半段来查询:
 
$ git-cat-file -t 54196cc2
commit
$ git-cat-file commit 54196cc2
tree 92b8b694ffb1675e5975148e1121810081dbdffe
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500

initial commit
   
    树(tree)可以指向一个或多个“blob”对象,每个“blob”对象和一个文件相对应。此外,树也可以指向其它树对象(tree objects),这样就可以创建一个目录层次结构。你可以用ls-tree命令来观察树的内容(记住:可以用一个足够长的SHA1值的前半段来查询):
 
$ git ls-tree 92b8b694
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad    file.txt
 
     我们看到在这个树中有一个文件。这个树的SHA1值指向那个文件的数据内容:
$ git cat-file -t 3b18e512
blob
    一个“blob”就是一个文件数据内容。我们也可以用cat-file命令来观察“blob”对象:
$ git cat-file blob 3b18e512
hello world
    注意这里显示的只是文件旧的数据内容;所以,当Git给初始状态的树建立一个命名对象的时候,这个树对象只是纪录了在第一次提交时这个目录结构状态的快照。
    所有的这些对象按照它们的SHA1名字被存储在Git的目录下面:
$ 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
    这些文件的内容只是压缩过的文件数据内容加上一个标识文件长度和类型的文件头。文件的类型有4种:blob,树(tree),提交(commit)和标记(tag)。
    最容易找到的提交(commit)是HEAD提交(HEAD commit),它被存放在.git/HEAD:
$ cat .git/HEAD
ref: refs/heads/master
 
   这个命令显示了我们当前正处在哪一个分支(branch)上(译注:在这个例子中,当前处在master分支上)。在.git目录下面也按照这个路径(refs/heads/master)存放了一个命名文件,而这个文件(译注:.git/refs/heads/master)包含了一个指向提交对象(commit object)的SHA1值,我们可以用cat-file查看它:
$ 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 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500

add emphasis
 
   这里的树对象(tree object)指向了树的新状态:
$ git ls-tree d0492b36
100644 blob a0423896973644771497bdc03eb99d5281615b51    file.txt
$ git cat-file blob a0423896
hello world!
   
    父(parent)对象指向前一次提交:
$ git-cat-file commit 54196cc2
tree 92b8b694ffb1675e5975148e1121810081dbdffe
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500

initial commit

    这个树对象就是我们最初查看的树,这个最初的提交对象特殊的地方在于他没有父对象。

    大多数提交都仅有一个父对象,但是很多时候一个提交也可能有多个父对象。如果一个提交代表了一个合并(merge)操作,这个提交就会有多个父对象,而那些父对象指向了各个被合并的分支的头。

    除了blob,树(tree)和提交(commit)之外,还有一种Git对象:标记(tag),本教程不讨论标记(tag);详情请参考git-tag(1)。

    到目前为止,我们已经学习了Git怎样用对象数据库来描述一个工程的历史:

  • “提交”对象指向“树”对象来表现一个目录在某个特定历史点的快照;同时,“提交”对象指向“父”提交对象来显示它们在工程历史中的轨迹。
  • “树”对象表现单个目录的状态,把目录名字和一堆“blob”对象或“树(tree)”对象联系在一起;其中“blob”对象包含文件数据内容,而树对象包含子目录信息。
  • “blob”对象包含文件数据内容(但不包含任何其它的结构信息)。
  • ./git/refs/heads/存放了所有分支(branch)的头提交对象的引用。
  • 当前分支(branch)的名字存放在./git/HEAD。
    注意,很多Git命令用树作为参数。但是,正如我们在前面看到的,可以用多个不同的方法来引用一个树:用这个树的SHA1值,用指向这个树的提交对象的名字,用分支名字(如果这个分支的头指向这个树)。。。而且这些方法都适用于大多数的Git命令。
    在Git命令说明中,“tree-ish”用来统一指代这些作为参数的树。

索引文件(index file)

    我们用来创建提交的主要工具是“git commit -a”,这个命令会创建一个包含所有在工作树上的变动的提交。但是,如果你只是希望提交某些文件的改动,或者只是希望提交某些文件的某些改动,你应该怎样做呢?
    继续我们的示例工程,让我们再次修改file.txt:
 
$ echo "hello world, again" >>file.txt
   
    但这次我们先不马上提交,而是加一个中间步骤:用diff来跟踪发生了什么:
$ git diff
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 hello world!
+hello world, again
$ git add file.txt
$ git diff
    最后一次diff的结果显示为空,而我们还没有做任何提交操作,头(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”命令不是和头(HEAD)比较,而是和“某种其它的东西”在比较。事实上这里说的“某种其它的东西”就是索引文件(index file)。索引文件以二进制的形式存放在.git/index,我们可以用ls-files来查看索引文件:

$ git ls-files --stage
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0       file.txt
$ git cat-file -t 513feba2
blob
$ git cat-file blob 513feba2
hello world!
hello world, again

    可以看出,“git add” 命令新建了一个blob对象,然后放了一个指向这个blob对象的引用在索引文件中。如果我们再次修改file.txt的内容,“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?
    通过使用相应的参数,git diff也可以显示工作目录和最后提交(译注:即HEAD)的差异(译注:用git diff HEAD),或者显示索引文件和最后提交的差异(译注:git diff --cached):
$ 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选项)创建一个新的提交到头(HEAD)上,然后可以验证这次提交状态中包含的变动只是那些被保存在了索引文件里的变动,而还只在工作目录中的变动不会被提交到HEAD上去。
$ 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默认用索引文件来创建提交,而不是用当前的工作树来创建提交。但如果用了-a选项的话,会先把所有在工作树上的变动更新到索引文件,然后再从索引文件提交那些变动到头(HEAD)上。
    最后,我们研究一下git-add作用在索引文件上的效果:
$ echo "goodbye, world" >closing.txt
$ git add closing.txt

    git-add的作用就是在索引文件中加入新的一项:

$ git ls-files --stage
100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0       closing.txt
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0       file.txt
    然后你可以用cat-file查看这条新的纪录指向了当前文件的内容:
$ git cat-file blob 8b9743b2
goodbye, world

“status”命令是一个快速获得当前概况的方法:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file: closing.txt
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified: file.txt
#

 
    既然close.txt文件的当前状态被缓存在索引文件中,它被标记为“Changes to be commited”。而由于file.txt在工作目录里面有一个变化还没有被反映到索引文件里去,它被标记为“changed but not updated”。此时,运行git-commit命令将会创建一个新的提交来把close.txt加入到头(HEAD)上去,但是不会把file.txt的变动更新到头(HEAD)上(译注:因为索引文件中还没有包含file.txt的最新变动)。
 
    注意:不带参数的git diff会显示file.txt的变动,但是不会在close.txt那边显示任何差异;因为索引文件中的close.txt的版本和当前工作目录中版本是同一个版本。
 
    除了作为新的提交的中间阶段,在checking out分支的时候,索引文件也被用来组织对象数据库;而在做合并的时候索引文件也被用来保持树对象。可以在 core tutorial 和相关的参考手册中查看索引文件更多的细节。
 

进一步?

现在你已经具备了阅读所有的git命令参考手册所需的知识;通过手册先了解Everyday git里面提到的Git命令是一个好的开始。 你能在 Glossary 里面查询所有不了解的术语.

Git User's Manual 提供了对Git更深入的介绍.

CVS migration 解释了怎样把CVS库(CVS repository)导入到Git里面,以及怎样用类似CVS的方式使用Git。

howtos 提供了一些关于Git使用的有趣的例子.

Git的开发人员, Core tutorial 深入了关于Git机制的底层细节,比如创建一个新提交的底层机制.

 

Last updated 20-May-2007 09:08:18 UTC
继续阅读
  • 如何在一台电脑上安装和引导145个操作系统

    1# 如何在一台电脑上安装和引导145个操作系统 我保证是在 这个思路之后写的“指南” 这145个系统是:- 3 Dos 5 Windows 137 Linux 我也在一台 Darwin x86 电...

  • 十大最佳极客言论

    译者:看似滑稽、实则讽刺。有几句不太好理解,需要相关的一些背景知识。附上原文,以便理解的比我透彻的人修改。 1、There are 10 types of people in the world: t...

  • 最好的五个文本编辑器

    本文的原作者通过在自己的网站上放置投票器,试图通过投票来产生人们心目中最好的5个文本编辑器

  • Songbird - 桌面/网络混血儿将会起飞吗?

    音乐使我们快乐。我们如此快乐,我们挥舞着双手笑着,每年数十亿美元花费于此。所以音乐是成为网上最受欢迎的媒体形式并非偶然。 因为音乐市场很庞大,存在着创新的机会。最近我们看到大量的新服务比如last....

  • 云计算——IBM的未来策略

           IBM计划建立一个相当规模的商业模式,为主流的企业客户引入Google风格的计算方式。   今天,IBM宣布了自己的策略,在大型数据中心方面进行有意义的技术开拓工作,激发大家的商业兴趣...

  • 7.0版女友程序V1.0版太太程序

    当你把女友升级为太太时,会发生什么呢? 看看这个故事吧

  • 《大教堂与市集》全文中译版(The Cathedral and the Bazaar)

    大教堂与市集 The Cathedral and the Bazaar 埃里克·斯蒂芬·雷蒙 (Eric Steven Raymond)【著】 刘安辙(Angelo Liu)【译】   *一 大教堂...

  • 为什么每个人都应该尝试Ubuntu

    如果您是位狂热的 Gentoo 或者 Slackware 粉丝,请离开,不要继续读下去。您可能不会像我说的那样。(但是向您保证,我尊敬并欣赏您的目标,致力于运行您自己可以完成的简单的 GNU/Lin...

标签:

内容有问题?请与我们联络。

译作评分

  • Currently 0.00/5
  • 1
  • 2
  • 3
  • 4
  • 5
 0.0  |  0 个评分

2条评论    0眉批

  • 1.

    et 举人

    这是Git很重要的一个文档。中英文水平都很烂,凑合翻了翻,欢迎指正。。。

    05/03/2008

  • 2.

    et 举人

    还有,其实这篇文章只讲了两个简单的东西:
    1。git里面的有4种对象;
    2。git里面的commit命令是从index  file提交到HEAD上,要先把working  目录的变动更新到index  file,以后才能再把index  file的更新提交到HEAD上。

    其中:
    git-diff  比较工作目录和索引文件
    git-diff  HEAD  比较工作目录和HEAD
    git  diff  --cached  比较index和HEAD

    05/03/2008

添加评论

欢迎访问译言网。在这里,您可以。。。

阅读
发现
翻译