前言
写这篇文章一开始主要是受到了 Write Yourself a Git, 深入理解Git实现原理 这两篇文章的激励. 俗话说学会使用轮子的最佳方法就是造一个轮子(其实是我刚刚说的), 而在使用 Python 实现的过程中又多少碰到了点前面两篇文章没有覆盖到的细节点, 特记录于此, 以飨读者.
本文章介绍顺序是先介绍其存储用的数据结构, 分别按其概念模型(包含了什么信息, 为什么要这样设计)及其二进制格式(储存在文件系统中的格式)来介绍; 随后再描述如何实现 Git 的常用功能.
本文章不是 Git 的教程, 如果读者对 Git 的操作并非十分了解的话可以参考 ProGit, 一本非常好的 Git 教材.
本文对于细节扣得有点多, 可能并不适合只想理解 Git 底层模型的读者.
或者也可以像我一样现学现写.
本文的配套代码及 Markdown 源码一同发布于 GitHub: mgit, 可以配套查看. (顺着 Commit 看我怎么挣扎着理解的说不定别有一番乐趣).
由于作者水平有限, 时间亦有限, 错漏难免, 还请大佬指正.
目录
- 目录 & 前言 & Git repo
- Object 文件
- Stage / 暂存区
- 指令: 底层与高层
- 结语
个人总结
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 文件