Loading... # 自己实现 Git 版本控制系统技术分析 # 一、概述 ## 1. 项目背景 ### A. 作者动机 版本控制对许多开发者来说是一个黑盒,不理解文件如何存储、差异如何生成、提交如何构建。作者出于学习和造轮子的目的,决定自己实现一个简化版的 Git 系统。 ### B. 核心问题 - Git 内部如何存储文件 - 如何生成差异对比 - 提交对象如何构建和关联 ## 2. 项目目标 ### A. 学习目标 深入理解 Git 的内部工作原理,特别是内容寻址文件存储机制。 ### B. 技术选型差异 - 使用 SHA-256 替代 Git 的 SHA-1 - 使用 zstd 压缩替代 Git 的 zlib - 不追求与 Git 兼容 # 二、核心概念分析 ## 1. Git 对象模型 Git 基于三种核心对象类型: ### A. Blob 对象 存储文件内容的压缩数据,以内容的哈希值作为唯一标识。 ### B. Tree 对象 存储目录结构,包含文件名和子目录及其对应的哈希值列表。 ### C. Commit 对象 存储提交信息,包含: - Tree 哈希(文件系统快照) - 父提交哈希(历史链) - 作者信息 - 提交消息 ```mermaid graph TB subgraph "提交对象结构" Commit[Commit 对象] Commit --> Tree[Tree 哈希] Commit --> Parent[父提交哈希] Commit --> Author[作者] Commit --> Message[提交消息] end subgraph "Tree 对象结构" Tree --> Blob1[Blob 哈希 1] Tree --> Blob2[Blob 哈希 2] Tree --> SubTree[子 Tree 哈希] end subgraph "Blob 对象结构" Blob1 --> Content1[文件内容 1] Blob2 --> Content2[文件内容 2] end Commit --> Tree Tree --> Blob1 Tree --> Blob2 ```  ## 2. 内容寻址存储 Git 本质上是一个键值存储系统: ``` key = hash(content) value = compressed(content) ``` 这种设计的优势: - 相同内容自动去重 - 历史版本高效存储 - 完整性校验内置 # 三、技术实现 ## 1. 项目命名 项目命名为 TVC(Tony's Version Control),使用以下命名约定: - `.tvc` 目录替代 `.git` - `.tvcignore` 文件替代 `.gitignore` ## 2. 核心功能模块 ### A. 命令列表 - `ls`:列出非忽略文件并计算哈希 - `commit`:创建提交对象 - `checkout`:检出指定提交 ### B. 文件遍历实现 使用 Rust 递归读取目录,跳过忽略文件: ```rust // 核心逻辑 let cb = |path: &Path| { let hash = sha256::try_digest(&path) .unwrap_or("<invalid hash>".to_string()); println!("{}\t{}", path.display(), &hash); }; read_dir_recursive(Path::new("./"), &ignore_rules, &cb).unwrap(); ``` ### C. 压缩与解压缩 使用 zstd 库进行压缩和解压缩: ```rust // 压缩文件 fn compress_file(source: &Path, dest: &Path) -> std::io::Result<()> { let input = File::open(source)?; let output = File::create(dest)?; let mut encoder = zstd::Encoder::new(output, 3)?; std::io::copy(&mut &input, &mut encoder)?; encoder.finish()?; Ok(()) } // 解压缩对象 fn decompress_object(object: &str) -> std::io::Result<String> { let path = PathBuf::from(format!("./.tvc/objects/{}", object)); let object = File::open(path)?; let mut buf: String = String::new(); let mut decoder = zstd::Decoder::new(object)?; decoder.read_to_string(&mut buf)?; decoder.finish(); Ok(buf) } ``` ## 3. 提交对象生成 ### A. 提交格式 ``` commit\0 tree\t<tree>\n parent\t<parent>\n author\t<author>\n message\t<message>\n ``` ### B. 生成流程 ```mermaid sequenceDiagram participant User as 用户 participant TVC as TVC participant FileSystem as 文件系统 participant Objects as .tvc/objects/ User->>TVC: tvc commit "message" TVC->>FileSystem: 读取工作目录 TVC->>TVC: 生成 Tree 对象 loop 每个文件 TVC->>TVC: 计算哈希 TVC->>TVC: 压缩内容 TVC->>Objects: 存储 Blob end TVC->>TVC: 创建 Commit 对象 TVC->>Objects: 存储 Commit TVC->>FileSystem: 更新 HEAD TVC->>User: 返回提交哈希 ```  ### C. 代码实现 ```rust let message = args.iter().skip(1) .map(|s| s.as_str()) .collect::<Vec<_>>() .join(" "); let author = "god"; let parent_hash = head; let tree = generate_tree(Path::new("./"), &ignore_rules) .expect("tree error"); let tree_hash = digest(tree); let commit_object = format!( "commit \0tree\t{}\nparent\t{}\nauthor\t{}\nmessage\t{}", tree_hash, parent_hash, author, message ); let commit_hash = digest(&commit_object); let dest = PathBuf::from(format!("./.tvc/objects/{}", commit_hash)); // 压缩并存储... fs::write("./.tvc/HEAD", &commit_hash)?; ``` # 四、数据结构设计 ## 1. Commit 结构 ```rust #[derive(Debug, Clone)] struct Commit { tree: String, parent: String, author: String, message: String, } ``` ## 2. Tree 结构 ```rust #[derive(Debug, Clone)] struct Tree { trees: Vec<(String, Tree)>, // (path, Tree) blobs: Vec<(String, String)>, // (path, hash) } ``` ## 3. 检出实现 ```rust impl Tree { fn generate_fs(&self, path: &Path) -> std::io::Result<()> { fs::create_dir(&path).unwrap_or(()); for (dirname, blob) in &self.blobs { let file = decompress_object(blob)?; fs::write(path.join(dirname), file.as_bytes())?; } for (dirname, tree) in &self.trees { tree.generate_fs(&path.join(dirname))?; } Ok(()) } } ``` # 五、技术对比 ## 1. 哈希算法对比 | 特性 | Git (SHA-1) | TVC (SHA-256) | |------|-------------|---------------| | 输出长度 | 160 位 | 256 位 | | 安全性 | 已破解 | 安全 | | 性能 | 更快 | 稍慢 | | 兼容性 | Git 标准 | 不兼容 | ## 2. 压缩算法对比 | 特性 | Git (zlib) | TVC (zstd) | |------|------------|------------| | 压缩比 | 较低 | 更高 | | 压缩速度 | 快 | 可配置 | | 解压速度 | 快 | 更快 | | 资源占用 | 低 | 中等 | # 六、设计权衡 ## 1. 简化设计 - 不区分作者和提交者 - 不存储对象大小 - 使用简单的文本格式而非二进制 ## 2. 潜在改进 作者认为如果重新实现,会使用 YAML 或 JSON 等结构化格式存储对象信息,使解析更简单。 ## 3. 未实现功能 - 合并(merge) - 变基(rebase) - 分支管理 - 远程仓库 # 七、核心洞察 通过这个项目,作者验证了 Git 的本质认知: > Git 本质上就是一个内容寻址的文件存储(content-addressable file store),一个键值数据库。 最困难的部分实际上是对象格式解析,而不是核心逻辑本身。 # 八、项目总结 ## 1. 技术收获 - 深入理解 Git 内部机制 - 掌握 Rust 文件操作和压缩库使用 - 体验从零设计版本控制系统 ## 2. 适用场景 - 学习 Git 内部原理 - 理解内容寻址存储模式 - 作为简化版本控制系统的参考实现 ## 3. 局限性 - 不适合生产使用 - 缺少关键功能(分支、合并) - 解析逻辑较为脆弱 *** ## 参考资料 1. [I made my own git - TonyStr's Blog](https://tonystr.net/blog/git_immitation) 2. [Git Internals - Git Objects](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) 3. [TVC Source Code - GitHub](https://github.com/tonystr/t-version-control) 4. [HackerNews Discussion](https://news.ycombinator.com/item?id=46778341) 最后修改:2026 年 01 月 28 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏