前言

写这篇文章一开始主要是受到了 Write Yourself a Git, 深入理解Git实现原理 这两篇文章的激励. 俗话说学会使用轮子的最佳方法就是造一个轮子(其实是我刚刚说的), 而在使用 Python 实现的过程中又多少碰到了点前面两篇文章没有覆盖到的细节点, 特记录于此, 以飨读者.

本文章介绍顺序是先介绍其存储用的数据结构, 分别按其概念模型(包含了什么信息, 为什么要这样设计)及其二进制格式(储存在文件系统中的格式)来介绍; 随后再描述如何实现 Git 的常用功能.

本文章不是 Git 的教程, 如果读者对 Git 的操作并非十分了解的话可以参考 ProGit, 一本非常好的 Git 教材.

本文对于细节扣得有点多, 可能并不适合只想理解 Git 底层模型的读者.

或者也可以像我一样现学现写.

本文的配套代码及 Markdown 源码一同发布于 GitHub: mgit, 可以配套查看. (顺着 Commit 看我怎么挣扎着理解的说不定别有一番乐趣).

由于作者水平有限, 时间亦有限, 错漏难免, 还请大佬指正.

目录

  1. 目录 & 前言 & Git repo
  2. Object 文件
  3. Stage / 暂存区
  4. 指令: 底层与高层
  5. 结语

个人总结

Git 的设计理念其实深受 Unix 哲学的影响:

  • 将文件系统本身作为一个 键值对数据库
  • 数据本身作为文本流而存储/处理 (虽然并非完全如此)
  • 并不使用专门的数据结构以压缩存储空间, 而是使用文本+泛用的压缩算法
  • 后端结构非常简单, (但对用户前端的抽象没做好emmm), 核心算法简单到基本不可能有错
  • 暴露出可以访问底层结构的命令行接口
  • 数据储存使用分隔符作为 Field 间的分隔而不是定长 (虽然也并非完全如此)

.git 目录结构

Git 将目录分为三个区:

  • 工作目录 (working directory): 即目录中除了 .git 目录之外的所有文件, 也就是我们平时写代码的地方
  • 暂存区 (index or staging area): 就是平时 add 完文件之后改动暂存的地方, 在 .git/index 下. 这里的改动会在下一次 commit 的时候被加入到 repo 里
  • Git 仓库 (Git Repository): 存放 git 的所有信息的地方, 也就是 .git 目录

Git Repository

$ tree .git
.git
├── branches         # 
├── COMMIT_EDITMSG   # 最近 Commit 时打的 Commit Message. 提供这个文件主要是为了与各种 editor 交互
├── config           # 配置文件
├── description      # 仓库描述文件
├── HEAD             # 当前
├── hooks            # 
├── index            # 暂存区文件
├── info             # 
│   └── exclude
├── logs             # 日志
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects         # 对象数据库
│   ├── 7e
│   │   └── f4c762de36ab4569c8f8bd0be86c871e68cbc9
│   ├── info
│   └── pack        # 压缩
└── refs            # 引用
    ├── heads
    │   └── master
    └── tags

其中config文件是一个语法类似INI文件的配置文件, 例子:

[core]
    # 仓库格式版本(似乎一直是0就没变过)
	repositoryformatversion = 0
    # 是否记录文件权限
	filemode = false
    # 是否允许没有 workpath 
	bare = false
	logallrefupdates = true
	# 文件系统是否支持 符号链接
    symlinks = false
    # 是否忽略大小写
	ignorecase = true

杂项: Python 相对路径 import

不知道查了多少次了 :(

一句话: 用相对路径 import 的文件就不要运行. 要运行的文件就不要用相对路径.

可以将用相对路径的文件xxx.py放进包里, 然后在包外run_xxx.py内使用包名引用. 此时必须使用python -m run_xxx.py.

下一篇: Object 文件