借助AI学习开源代码git0.7之六write-tree

write-tree.c 的作用是根据当前的索引(cache)内容创建一个树(tree)对象,并将其写入Git的对象数据库。
树对象代表了项目在某个时间点的目录结构。

代码的主要逻辑:

  1. main 函数:

    • 通过 read_cache() 读取索引(.git/index)的内容到 active_cache 中。
    • 检查索引中是否存在未合并(unmerged)的文件。如果存在,则报错并退出,不能无法为一个包含冲突的索引创建树对象。
    • 调用 write_tree() 函数来递归地创建树对象。
    • write_tree() 的初始调用传入了
      active_cache(所有索引条目的数组)、条目总数、一个空字符串作为基础路径(base)以及0作为基础路径长度(baselen)。这表示从项目的根目录开始处理。
    • 最后,将返回的顶层树对象的SHA-1值以十六进制格式打印到标准输出。
  2. write_tree() 函数:

    • 这是一个递归函数,负责将一个目录(及其子目录)的内容转换成一个Git的树对象。
    • 它遍历 cachep 数组中的索引条目。
    • 对于每个条目,它会检查该条目是否属于当前正在处理的目录(由 base 和 baselen 定义)。
    • 处理子目录: 如果一个条目的路径中包含 /,说明它位于一个子目录中。这时,函数会递归调用 write_tree()
      来为这个子目录创建树对象。递归调用会传入剩余的索引条目、更新后的基础路径和路径长度。递归调用返回后,write_tree 会将子目录作为一个特殊的条目(模式为
      S_IFDIR)写入当前正在生成的树对象中。
    • 处理文件: 如果条目是一个文件,它会直接将文件的模式(mode)、文件名和SHA-1值格式化后添加到一个缓冲区(buffer)中。
    • 在将任何对象(文件或子目录)添加到树之前,它会调用 check_valid_sha1() 来确保该对象确实存在于对象数据库中。
    • 当一个目录中的所有条目都处理完毕后,write_tree() 会调用 write_sha1_file()。这个函数会计算缓冲区的SHA-1值,并将缓冲区的内容(即树对象)以 “tree”
      类型写入对象数据库。
    • 函数返回它处理的索引条目数量。
  3. check_valid_sha1() 函数:

    • 这是一个辅助函数,通过 sha1_file_name() 构建对象文件的路径,然后使用 access() 系统调用检查该文件是否存在且可读。这是一种快速验证对象是否有效的方法。

编码技巧

write-tree.c 的代码虽然不长,但包含了一些非常经典和高效的C语言编程技巧,这些技巧在早期Git的性能和简洁性方面起到了关键作用。

1. 将扁平数据结构递归地转换为树状结构

这是整个文件的核心算法,也是最巧妙的地方。

  • 输入:一个扁平的、已按字母顺序排序的文件路径列表(active_cache)。
  • 输出:一个代表目录层级的树(tree)对象的SHA-1值。
  • 技巧:代码没有在内存中构建一个显式的树形数据结构(例如,使用struct Node和子节点指针)。相反,它通过对已排序的扁平列表进行一次遍历和递归来直接生成树对象。
    • write_tree 函数通过 base 和 baselen 这两个参数来追踪当前正在处理的目录“前缀”。
    • 因为它知道列表是排序的,所以一旦遇到一个文件路径的前缀与 base 不匹配了(memcmp(base, pathname, baselen)),就意味着当前目录的所有条目都已处理完毕。
    • 当遇到一个包含子目录的路径时(例如,在处理 base=“src/” 时遇到了 src/a/main.c),它会识别出 a 是一个子目录,然后递归调用 write_tree,并将新的 base 设置为
      “src/a/”。这种递归调用处理了子目录,然后将子目录的树对象SHA-1返回,父目录再将这个子目录作为一个条目记录下来。

这个方法极大地节省了内存,并且因为只遍历一次数据,所以非常高效。

2. 高效的指针和字符串操作

代码中大量使用了指针运算来处理字符串(文件路径),避免了不必要的内存分配和字符串拷贝,这是C语言性能优化的关键。

  • 计算子路径:filename = pathname + baselen; 这一行不是创建一个新的子字符串,而是简单地将指针向前移动 baselen 个字节,直接指向了相对于当前目录的文件名部分。
  • 计算路径长度:dirname - pathname 直接通过两个指针的地址差来计算子目录的长度,非常快。
  • 查找字符:使用 strchr(filename, ‘/’) 来快速定位下一个子目录分隔符。

3. 动态内存管理(“猜测并增长”策略)

在构建树对象的内容时,最终的大小是未知的。代码采用了一种常见的策略:

  1. 初始猜测:size = 8192; buffer = xmalloc(size); 先分配一个比较合理的初始大小(8KB)。
  2. 检查并扩展:在每次向 buffer 添加新内容之前,都会检查剩余空间是否足够 (if (offset + entrylen + 100 > size))。
  3. 按需重新分配:如果空间不足,就使用 xrealloc 来扩展缓冲区。alloc_nr是个宏或函数,用于计算一个更合适的、更大的新尺寸,以避免频繁地进行 realloc。

这种模式在处理未知大小的输出时非常高效和灵活。

4. “数组切片”式的递归

在递归调用 write_tree 时,注意这一行:
write_tree(cachep + nr, maxentries - nr, …)

这里没有为递归调用创建一个新的数组副本。cachep + nr 只是将指向 cachep 数组第 nr
个元素的指针传递给下一层函数。
这相当于在说:“请处理从这个点开始的剩余部分”。
这是一种零开销的“数组切片”方法,是C语言中处理数组子集的标准高效技巧。

5. 混合数据格式的直接构建

Git的树对象是一种混合了文本和二进制的格式:"<%mode> <%filename>\0<%20_byte_sha1>".

代码非常直接地构建了这个格式:

  1. sprintf(buffer + offset, “%o %.*s”, mode, entrylen, filename); 用 sprintf 来格式化文本部分(模式和文件名)。
  2. buffer[offset++] = 0; 手动添加空字符(\0)分隔符。
  3. memcpy(buffer + offset, sha1, 20); 用 memcpy 直接拷贝20字节的二进制SHA-1值。

这种方法避免了任何中间转换或复杂的格式化库,直接在内存中按字节构建出最终需要的数据,速度极快。

6. 编码技巧总结

write-tree.c 的代码是底层系统编程的一个绝佳范例。它展示了如何:

  • 用巧妙的算法设计(递归处理排序列表)来避免复杂的数据结构和内存开销。
  • 通过精细的指针操作来最大化字符串处理效率。
  • 在C语言中有效地管理动态增长的内存缓冲区。

这些技巧共同造就了一个非常快速和资源节约的程序,这对于像Git这样的性能敏感的工具来说至关重要。

总结

write-tree 的核心思想是:

  • 从一个扁平的、按字母顺序排序的索引条目列表开始。
  • 通过递归地处理路径,将这个扁平的列表转换成一个分层的树结构。
  • 每个子目录都变成一个新的树对象,而文件则作为blob对象被引用。
  • 最终,整个项目的文件结构被表示为一个顶层的树对象,其SHA-1值就是这次操作的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92363.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92363.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92363.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源 python 应用 开发(八)图片比对

最近有个项目需要做视觉自动化处理的工具&#xff0c;最后选用的软件为python&#xff0c;刚好这个机会进行系统学习。短时间学习&#xff0c;需要快速开发&#xff0c;所以记录要点步骤&#xff0c;防止忘记。 链接&#xff1a; 开源 python 应用 开发&#xff08;一&#xf…

SeaTunnel 云仓连接器使用指南 | AI 助手解读系列

最近体验了一下 Deepwiki 的 AI 文档生成功能&#xff0c;本文展示其自动生成的《SeaTunnel 云端数据仓库连接器》文档内容&#xff0c;欢迎大家一起“挑刺捉虫”&#xff0c;看看 AI 写技术文档到底靠不靠谱&#xff1f; 本文档介绍了 Apache SeaTunnel 的云数据仓库连接器&a…

每日算法刷题Day51:7.21:leetcode 栈6道题,用时1h40min

二.进阶 1.套路 2.题目描述 1.给你一个字符串 s 。它可能包含任意数量的 * 字符。你的任务是删除所有的 * 字符。 当字符串还存在至少一个 * 字符时&#xff0c;你可以执行以下操作&#xff1a; 删除最左边的 * 字符&#xff0c;同时删除该星号字符左边一个字典序 最小的字…

网络基础DAY16-MSTP-VRRP

STP/RSTP的局限性1.所有VLAN共享一棵生成树 2.无法实现不同VLAN在多条Trunk链路上的负载分担 3.次优化二层路径。MSTP的基本概念及优势MSTP的定义MST域拥有相同MST配置标识的网桥构成的集合。 具体如何分辨是否是同一个域&#xff0c;就看域名&#xff0c;配置修订号&#xff0…

freertos关键函数理解 uxListRemove

//删除pxItemToRemove节点 UBaseType_t uxListRemove(ListItem_t *pxItemToRemove) { //The list item knows which list it is in. Obtain the list from the list item.//找到节点所在的链表//my_printf( "uxListRemove pxItemToRemove %#p\n", pxI…

C语言---番外篇(柔性数组)

前言&#xff1a; 由于这块内容所谓综合性比较高&#xff0c;有数组的知识&#xff0c;有结构体的知识&#xff0c;还有动态内存管理的知识&#xff0c;所以我就单独写一篇博客&#xff0c;此谓番外篇。 柔性数组的概念 定义在结构体的最后一个元素的位置且大小未知的数组就叫…

单片机的几种GPIO输入输出模型详解

模式选择汇总参考表&#xff1a;模式输出驱动输入阻抗默认状态典型应用场景推挽输出强驱动禁用可配置LED, SPI, 高速信号开漏输出弱驱动禁用低/悬空IC, 电平转换, 线与浮空输入禁用极高不确定外部强驱动信号上拉输入禁用中高高电平按键(接地型), 数字输入下拉输入禁用中高低电平…

深度解析ECharts.js:构建现代化数据可视化的利器

引言&#xff1a;数据可视化的新时代挑战 在数字化转型浪潮中&#xff0c;数据可视化已成为企业决策和用户体验的关键环节。面对海量数据的呈现需求&#xff0c;传统表格已无法满足用户对直观洞察的渴求。作为百度开源的JavaScript可视化库&#xff0c;ECharts.js凭借其强大的功…

从零构建实时通信引擎:Freeswitch源码编译与深度优化指南

一、构建工具&#xff1a;编译FreeSWITCH及其依赖库的基础 1. CMake2. Autoconf 二、汇编器&#xff1a;提升音视频处理性能 3. YASM / NASM 三、音视频编解码器&#xff1a;支撑实时媒体传输 4. Opus5. x264 (可选)6. libvpx / libvpx2 (可选) 四、多媒体框架与工具库&#xf…

网络原理 HTTP 和 HTTPS

目录 一 . HTTP 协议 二 . 抓包 三 . HTTP 请求 / 响应的基本格式 &#xff08;1&#xff09;HTTP请求的基本格式 &#xff08;2&#xff09;HTTP响应的基本格式 四 . HTTP 方法 GET 和 POST 的区别&#xff1a; 五 . 请求报头和响应报头 &#xff08;1&#…

基于单片机的自动条幅悬挂机

摘 要 随着日新月异科技发展&#xff0c;在心率体温测量方面&#xff0c;我们取得了迅速的发展&#xff0c;就近日而言&#xff0c;脉搏测量仪已经在多个领域大展身手&#xff0c;除了在医学领域有所建树&#xff0c;在人们的日常生活方面的应用也不断拓展&#xff0c;如检疫…

《C++》面向对象编程--类(中)

文章目录一、构造函数1.1定义1.2语法1.3特性二、析构函数2.1定义2.2语法2.3特性三、拷贝构造函数3.1定义3.2语法3.3特性3.4浅拷贝3.4.1定义3.4.2浅拷贝的风险3.5深拷贝一、构造函数 1.1定义 在C中&#xff0c;构造函数&#xff08;Constructor&#xff09; 是一种特殊的成员函…

机器学习初学者理论初解

大家好! 为什么手机相册能自动识别人脸&#xff1f;为什么购物网站总能推荐你喜欢的商品&#xff1f;这些“智能”背后&#xff0c;都藏着一位隐形高手——机器学习&#xff08;Machine Learning&#xff09;。一、什么是机器学习&#xff1f;简单说&#xff0c;机器学习是教计…

原码反码补码

在Java中&#xff0c;无论是小数还是整数&#xff0c;他们都要带有符号&#xff08;和C语言不同&#xff0c;C语言有无符号数&#xff09;。首位就作为符号位。原码反码&#xff1a;正数的反码是其原码本身负数的反码是在其原码的基础上, 符号位不变&#xff0c;其余各个位取反…

使用ubuntu:20.04和ubuntu:jammy构建secretflow环境

一、使用ubuntu:20.04构建隐语编译环境FROM ubuntu:20.04LABEL maintainer"build SecureProtocolLib on ubuntu:20.04"ARG TARGETPLATFORM# change dash to bash as default shell RUN ln -sf /bin/bash /bin/shRUN apt update \&& apt upgrade -y \&&am…

Hinge Loss(铰链损失函数)详解:SVM 中的关键损失函数

&#x1f4cc; 一、什么是 Hinge Loss&#xff1f;Hinge Loss&#xff08;铰链损失&#xff09;&#xff0c;是 支持向量机&#xff08;SVM, Support Vector Machine&#xff09; 中常用的一种损失函数&#xff0c;用于最大间隔分类。其核心思想是&#xff1a;当预测结果已经正…

days32 :零基础学嵌入式之网络2.0

一、wireshark &#xff1a;网络抓包工具1.功能&#xff1a;抓取通过电脑网卡的网络数据2.作用&#xff1a;排查故障、抓取数据做数据分析、3.用法&#xff1a;&#xff08;1&#xff09;sudo wireshark&#xff08;2&#xff09;选择需要抓取的网卡》any&#xff08;3&#xf…

数字护网:一次深刻的企业安全体系灵魂演练

&#x1f9e9; 引言&#xff1a;什么是“护网”&#xff1f;—— 不止是攻防&#xff0c;更是企业安全能力的年度大考 每年&#xff0c;由国家相关部门牵头的“护网行动”都如期而至&#xff0c;各大企事业单位的安全团队也随之进入高度戒备状态。然而&#xff0c;“护网”远非…

基于 NumPy 的高效数值计算技术解析与实践指引

在数据处理与科学计算领域&#xff0c;高效是核心诉求。NumPy 作为 Python 生态高效数值计算的基石&#xff0c;以高性能多维数组对象及配套函数&#xff0c;成为数据从业者的必备工具。其数组支持算术、比较、逻辑等丰富运算&#xff0c;通过向量化操作直接处理每个元素&#…

Kafka MQ 控制器 broker

Kafka MQ 控制器 broker 1 控制器broker的选举 在 Kafka 集群中会有一个或多个 broker,其中有一个 broker 会被选举为控制器(Kafka Controller)​,它负责管理整个集群中所有分区和副本的状态。当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本…