Git 是目前世界上最广泛使用的版本控制系统之一,它的诞生和发展对软件开发产生了深远的影响。今天,我们来深入解析 Git 最原始的开源版本代码,了解它是如何从最初的简单设计逐步发展成为如今强大的工具。

一.获取 Git 最初的源码

首先,我们需要获取 Git 的源码。可以通过以下命令克隆 Git 的官方仓库:

git clone https://github.com/git/git.git

然后,找到 Git 的第一个提交记录。使用以下命令查看最早的提交:

git log --date-order --reverse

找到第一个提交的哈希值 e83c5163316f89bfbde7d9ab23ca2e25604af290,切换到这个版本:

git checkout e83c5163316f89bfbde7d9ab23ca2e25604af290

二.初始版本的文件结构

在 Git 的第一个版本中,文件结构相对简单。主要文件如下:

  • cache.h:定义了核心数据结构,如 cache_entrycache_header

  • init-db.c:初始化 Git 仓库,类似于现在的 git init

  • update-cache.c:将文件添加到索引中,类似于现在的 git add

  • write-tree.c:根据索引生成树对象,类似于现在的 git commit

  • read-tree.c:读取树对象。

  • cat-file.c:查看对象内容。

  • show-diff.c:显示文件差异。

  • commit-tree.c:提交树对象。

  • Makefile:编译规则文件。

三.核心概念

在 Git 的最初版本中,已经定义了三个核心对象:blob、tree 和 commit。

  • Blob:表示文件的内容,是最基本的对象类型。

  • Tree:表示目录结构,包含文件名、模式和指向 blob 或其他 tree 的引用。

  • Commit:表示一次提交,包含树对象的引用、提交者信息和提交日志。

四.编译与运行

为了编译 Git 的初始版本,需要安装一些依赖库:

sudo apt install zlib1g-dev libssl-dev

然后修改 Makefile,确保包含正确的库链接:

LIBS= -lssl -lz -lcrypto

运行 make all 编译所有文件,生成可执行程序。

五.源码分析

5.1. cache.h

这是 Git 的核心头文件,定义了 Git 的基本数据结构和接口。

  • cache_header:定义了索引文件的头部结构,包含签名、版本号和条目数量。

  • cache_entry:表示索引中的每个文件条目,包含文件的元数据(如时间戳、文件大小、SHA1 哈希值等)。

  • 功能:为 Git 的索引机制提供了数据结构支持,是 Git 管理文件状态的基础。

5.2. init-db.c

用于初始化 Git 仓库,类似于现在的 git init

  • 功能:创建 .dircache 目录(后来的 .git 目录),并在其中创建 objects 文件夹,用于存储对象文件。

  • 核心代码

    mkdir(".dircache", 0755);
    mkdir(".dircache/objects", 0755);
  • 作用:为 Git 仓库创建基本的目录结构,准备存储对象和索引文件。

5.3. update-cache.c

用于将文件添加到索引中,类似于现在的 git add

  • 功能:计算文件的 SHA1 哈希值,将文件信息存储到索引文件中。

  • 核心代码

    sha1 = sha1_file(file, strlen(file), "blob");
  • 作用:将工作区的文件变更记录到索引中,为后续的提交操作做准备。

5.4. write-tree.c

根据索引生成树对象,类似于现在的 git commit

  • 功能:解析索引文件,生成树对象并存储到对象数据库中。

  • 核心代码

    write_tree_from_cache(tree, active_cache, active_nr);
  • 作用:将索引中的文件状态转换为树对象,为提交操作提供基础。

5.5. read-tree.c

用于读取树对象。

  • 功能:解析树对象,恢复目录结构。

  • 核心代码

    read_tree(tree);
  • 作用:从对象数据库中读取树对象,恢复目录结构,用于查看提交内容。

5.6. cat-file.c

查看对象内容。

  • 功能:根据 SHA1 哈希值读取对象内容,并输出到临时文件。

  • 核心代码

    read_sha1_file(sha1, type, size);
  • 作用:用于调试和查看对象的原始内容,类似于现在的 git cat-file

5.7. show-diff.c

显示文件差异。

  • 功能:比较工作区和索引中的文件差异。

  • 核心代码

    show_diff(active_cache, active_nr);
  • 作用:显示未提交的文件变更,类似于现在的 git diff

5.8. commit-tree.c

提交树对象。

  • 功能:基于树对象生成提交对象,并写入对象数据库。

  • 核心代码

    commit_tree(tree, parent, author, committer, message);
  • 作用:完成一次提交操作,记录提交信息和树对象的引用。

六.功能解析

6.1初始化仓库

init-db 是 Git 的第一个版本中用于初始化仓库的程序。它会在当前目录下创建一个 .dircache 目录,并在其中创建 objects 文件夹,用于存储对象文件。

./init-db

6.2添加文件到索引

update-cache 用于将文件添加到索引中。它会读取文件内容,计算其 SHA1 哈希值,并将文件信息存储到索引文件中。

./update-cache file.txt

6.3提交更改

write-tree 会根据索引生成一个树对象,并将其存储到对象数据库中。这个树对象包含了当前索引中所有文件的信息。

./write-tree

七.总结

Git 的最初版本虽然简单,但已经奠定了其核心设计理念。通过对象数据库和索引机制,Git 实现了高效的版本控制功能。这个版本的代码虽然只有约 1000 行,但它展示了 Linus Torvalds 的天才设计和对简洁性的追求。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐