Git 内部实现:.git

本文出处:http://robinnagpal.wordpress.com/2012/08/04/git-internals-git-folder/

如果你是那种只需要程序能够正常工作,不关心其内部实现的人,本系列文章不适合你。不过,我说这话并不是说我反对那些仅仅让程序正常工作的人的那种处世态度,我只是说,我不是这么一种人。

在这篇文章中,我将试图阐述 GIT 是如何追踪你的文件以及如何维护文件的版本的。在阅读本系列文章之后,你应该能够回答以下问题:

  1. 为什么说 GIT 是一个分布式的版本管理系统?
  2. 为什么 GIT 创建和切换分支是如此快速?
  3. 当你提交修改、创建分支、将代码从一个版本移动到另外一个版本的时候究竟发生了什么?

在此,我们假设你已经了解如何使用 GIT。我们先来讨论那些即便是对于其它的版本控制系统也是通用的术语:

  • Repository(仓库),这是你的项目保存的地方。这个文件系统(或者叫目录)完全知晓你想让版本控制系统追踪的那些文件的版本。
  • Commit(提交),这是你正在追踪的文件在某一特定时刻的快照。为了识别这些快照,版本控制系统通常会为此增加一个元数据。
  • Branch(分支),这是你希望以不同方式处理同一段代码的方式。一旦你创建了一个分支,它们就是彼此相互独立的;在创建分支之前,它们有一段共同的历史,之后则有各自的历史。

现在,我们开始创建一个仓库:

git init

我们在希望 GIT 追踪的目录执行这一命令。

GIT 仓库

执行过上面代码之后,我们在这个目录执行以下命令:

ls -latr

现在我们发现了一个名为 .git 的新目录。让我们看看这个目录中有什么:

refs
info
hooks
HEAD
description
config
branches
objects

所有由 GIT 追踪的文件都在 .git 有所反映。这个目录包含了你的文件的所有版本,你的代码的所有分支以及你希望 GIT 自动运行的脚本等。理解上面这些目录/文件能够帮助你理解 GIT 是如何高效的管理文件的,以及 GIT 是如何维护文件的不同版本的。

下面我们一个个来看看以上文件和目录。

refs – 这个目录包含所有到 tags、branches、stash 以及 head 的引用的信息。由于我们现在没有任何远程或本地分支,也没有任何备份或者标签,我们就不能看到这个目录的全部内容。不过,在一个典型的 GIT 仓库中,该目录应该有如下内容:

heads/
remotes/
tags/
stash

info – 这个目录中最明显的部分是一个名为 exclude 的文件。我们可以编辑这个文件来告诉 GIT 我们不愿提交或者追踪的文件的模式。

hooks – 这个目录包含一些我们希望在某些操作之前或者之后自动运行的脚本,比如我们希望在提交之前、提交之后、更新之后执行某些操作等。我们可以在这个文件夹中看到已经存在的示例:

applypatch-msg.sample
commit-msg.sample
post-commit.sample
post-receive.sample
post-update.sample
pre-applypatch.sample
pre-commit.sample
pre-rebase.sample
prepare-commit-msg.sample
update.sample

HEAD – 这个文件包含我们正在查看的当前 ref。在我们的例子中,因为我们是在 master 本身,我们可以看到这个文件就是指向 master 的引用:

ref: refs/heads/master

如果我们是在一个分支上,我们可能会看到这样的内容:

ref: refs/heads/production_fix

这里,production_fix 是一个分支的名字。

description – 正如其名字一样,这个文件包含的是我们所创建的仓库的描述信息。因为我们还没有输入任何信息,这个文件应该有类似下面的内容:

Unnamed repository; edit this file 'description' to name the repository.

有些 GIT UI 工具使用这个文件向用户展示该仓库的描述信息。

config – 这个文件包含了与仓库相关的配置信息。我们可以在这个文件追加信息,更新提交时所需的用户名和 email。现在我们的这个文件应该是这样的:

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true

branches – 旧版本的 GIT 所使用的,用来保存 git fetch、pull 或者 push 的 URL 信息。方法是在 branches/<name> 创建一个文件,使用这个 name 替代仓库参数。这个方式已经废弃不用

objects – 我们可以将此称为 GIT 仓库的数据库。GIT 主要将其内容以三种类型的对象保存起来:commit、tree 和 blob。我们将在以后的内容中详细讨论这些对象。

当我们使用 GIT 的时候,.git 文件夹中可能添加更多的文件。但是,我们上面介绍的内容已经足够让我们理解 GIT 仓库。

Leave a Reply