Git 是一个功能强大的分布式版本控制系统,它的核心概念决定了其强大的功能和灵活性。本章将深入探讨 Git 的基本概念,包括版本库、Git 对象类型、索引、可寻址内容名称、Git 追踪内容、路径名与内容的关系,以及 Git 的数据存储方式。此外,还会通过图示帮助读者更直观地理解 Git 的存储结构,并讲解 Git 工作时的核心概念,如 .git 目录、对象、blob、文件树、SHA-1 哈希值、树层次结构、提交、标签等。

4.1 基本概念

4.1.1 版本库

在 Git 中,版本库(Repository) 是用于存储项目所有文件及其历史记录的地方。Git 版本库的核心结构包括:

  1. 工作区(Working Directory):用户实际编辑文件的地方。
  2. 暂存区(Staging Area):Git 维护的一个中间区域,存储即将提交的更改。
  3. 版本库(Repository):Git 维护的所有版本的存储区域,通常在 .git 目录下。

在 Git 中,初始化一个新的版本库使用 git init 命令:

  1. git init

执行后,Git 会在当前目录下创建 .git 目录,该目录包含所有 Git 相关的文件和元数据,如对象存储、HEAD 指针、分支信息等。


4.1.2 Git 对象类型

Git 以对象(Object)为核心进行数据存储,主要包括以下三种对象类型:

  1. Blob(Binary Large Object):存储文件内容,Git 不存储文件的名字或路径,而是仅存储其内容。
  2. Tree(树):存储目录结构,记录文件与文件夹的组织方式,并指向对应的 blob 对象。
  3. Commit(提交):代表一次快照,包含提交信息、提交者、提交时间以及指向的 Tree 对象。

Git 使用 SHA-1 哈希 作为对象的唯一标识,每个对象在 .git/objects 目录下存储为一个以 SHA-1 哈希值命名的文件。


4.1.3 索引

索引(Index)是 Git 中的一个重要概念,也称为暂存区(Staging Area)。它的作用是充当 工作区和版本库之间的缓冲区,允许用户在提交之前整理文件的更改。

当运行 git add 命令时,文件的更改会被添加到索引中,而不是直接提交到版本库。例如:

  1. echo "Hello Git" > file.txt
  2. git add file.txt

此时,file.txt 进入索引,但尚未正式提交。可以通过 git status 查看索引中的内容:

  1. git status

要提交更改,需要运行 git commit

  1. git commit -m "Added file.txt"

4.1.4 可寻址内容名称

Git 的一个关键特性是所有存储的对象(blob、tree、commit)都使用 SHA-1 哈希进行标识。这意味着 Git 可以通过 SHA-1 哈希值来唯一地寻址任何存储的内容。例如,查看某个提交的哈希值:

  1. git log --oneline

输出可能类似于:

  1. a3c6f29 Initial commit

其中 a3c6f29 就是该提交的 SHA-1 哈希的前几位。Git 允许使用部分哈希值进行引用,例如:

  1. git show a3c6f29

4.1.5 Git 追踪内容

Git 通过跟踪文件的内容(而非文件本身)来管理版本变更。每当文件发生更改,Git 会计算其内容的 SHA-1 哈希值,并存储新的 blob 对象,而不是存储整个文件的副本。

查看 Git 追踪的文件状态:

  1. git status

文件可能处于以下几种状态:

  • Untracked(未跟踪):Git 尚未管理该文件。
  • Modified(已修改):文件已更改,但未添加到暂存区。
  • Staged(已暂存):文件已添加到暂存区,准备提交。
  • Committed(已提交):文件的更改已存入版本库。

4.1.6 路径名与内容

在 Git 中,路径名(文件名)与文件内容是分开的。Git 仅存储文件的内容,而文件名存储在 Tree(树)对象 中。这种设计使得 Git 可以高效地处理重命名文件的操作,因为 Git 只需更新树对象,而不必存储新的 blob。


4.1.7 打包文件

当 Git 仓库增长时,存储大量独立对象会导致 .git/objects 目录变得庞大。为了解决这个问题,Git 采用 打包(Packfile) 机制来合并多个对象,减少存储空间和提升性能。

手动运行以下命令可以进行打包优化:

  1. git gc

4.2 对象库图示

Git 的存储结构可以用下图表示:

  1. Commit -> Tree -> Blobs(文件内容)
  2. |
  3. -> Tree -> Blobs(文件内容)

每次提交(commit)指向一个树(tree),树指向多个 blob(文件内容)。如果文件未修改,则新的提交仍然指向原有的 blob,从而节省存储空间。


4.3 Git 在工作时的概念

4.3.1 进入 .git 目录

.git 目录是 Git 版本库的核心,包含:

  • objects/:存储 Git 对象(blob、tree、commit)
  • refs/:存储分支和标签
  • HEAD:指向当前分支
  • config:Git 配置文件

进入 .git 目录后,可以手动查看对象存储:

  1. cd .git/objects
  2. ls

4.3.2 对象、散列和 blob

.git/objects 目录下,每个对象文件名由 SHA-1 哈希值命名,例如 3b18e6。可以使用 git cat-file 命令查看对象内容:

  1. git cat-file -t 3b18e6
  2. git cat-file -p 3b18e6

4.3.3 文件和 tree

Tree 记录了文件的组织方式,每个提交(commit)都会生成一个新的 Tree,指向文件的 blob 对象。例如,查看某个提交的 tree:

  1. git ls-tree HEAD

4.3.4 对 Git 使用 SHA1 的一点说明

Git 使用 SHA-1 哈希 作为对象的唯一标识符,以确保数据完整性。如果文件内容被篡改,其 SHA-1 哈希值将发生变化,Git 立即能检测到。


4.3.5 树层次结构

Git 的存储结构采用类似 Unix 的层次化文件系统,每次提交都会生成新的 tree 结构,而不是存储整个文件快照。


4.3.6 提交

每次 git commit 都会创建一个新的提交对象,该对象指向当前 tree,并记录提交信息。例如,查看最近一次提交的详情:

  1. git show HEAD

4.3.7 标签

Git 标签(tag)用于标记特定的提交,例如创建版本发布标签:

  1. git tag v1.0

查看所有标签:

  1. git tag

结论

本章介绍了 Git 的核心概念,包括版本库、对象存储、索引、路径名与内容的关系、Git 的数据结构,以及 .git 目录的作用。掌握这些概念有助于深入理解 Git 的底层机制,为后续分支管理和协作开发打下基础。