Git 从 2.29 版起便能创建以 SHA-256 命名对象的仓库。[9] Git 核心也已取消这种本地存储格式的实验性标签。[2][6] 这两项事实很容易验证,也很容易被过度解读。截至 2026 年 8 月,Git 的 git-init 文档仍明确说明:默认算法是 SHA-1,SHA-256 仓库与 SHA-1 仓库尚不能互操作。[2] compatObjectFormat 相关功能仍处于未完成状态,供开发使用,尚未面向最终用户部署。[3]
对于生产链路仍依赖仅支持 SHA-1 的对端或工具链的既有仓库,眼下有价值的迁移动作是一场演练。正式仓库继续沿用当前格式;另建一个随时可删除的 SHA-256 仓库,合成足以代表真实历史的拓扑,包括合并、标签、notes 以及真实历史中存在的其他形态,再让这个金丝雀仓库经过每一个会接触生产环境的客户端与集成。逐处记录对象 ID 何时被当作不透明标识符,何时又被悄悄写死为“40 个十六进制字符”。即便正式切换仍在数年之后,这份审计也会立即产生价值。
Git 已规划的破坏性版本变更,让这项工作正当其时,同时也没有理由冒进。项目计划在未来一个大版本中,让新初始化仓库默认使用 SHA-256,并继续支持 SHA-1 对象格式。这份计划还把工具体系的准备度列为前置条件,优先于日历上的任何日期:各类库、应用程序与代码托管平台都必须理解新格式。[1]
图片背景:封面照片拍摄于 Git Merge 2022 会场,贡献者与用户因 Git 本身聚在一起,关注范围超出任何一家托管产品。这恰好对应此次迁移的尺度:一个对象标识符要先穿过由众多工具组成的社区,最后才会抵达人类读者。[8]
从一个随时可以删除的仓库开始
一个实用的实验室只需要常规 Git 命令,也不连接任何生产远端:
git init --object-format=sha256 sha256-lab
cd sha256-lab
printf '%s\n' 'object-format rehearsal' > README.md
git add README.md
git -c user.name='SHA-256 Lab' \
-c user.email='[email protected]' \
commit -m 'Create the rehearsal repository'
git rev-parse --show-object-format=storage
git rev-parse HEAD
git fsck --full
第一项检查应输出 sha256;完整 commit 名称应包含 64 个十六进制字符,SHA-1 的长度则是 40。查询仓库使用哪一种算法,应调用 git rev-parse --show-object-format=storage 这一接口,避免从单个值的长度猜测,也避免自行编写解析器读取 .git/config。[4]
这里发生的是对象命名空间的变化,远远超出给同一份历史换上更长标签的范围。Git 计算对象 ID 时,会把包含对象类型与长度的头部、一个 NUL 字节和序列化内容一并纳入哈希。tree、commit 与 tag 对象还包含其他对象的名称,因此,它们的 SHA-256 序列化形式会指向另一个对象命名空间。blob 对象的内容里没有子对象名称,但用来命名 blob 的算法变了,它的标识符也会随之改变。Git 的迁移设计包含往返转换,以及经过验证的两种名称映射;机械地给旧名称补位不在这套设计之内。[5]
仓库格式扩展还划出一道有意设置的界线。Git 会在仓库格式版本 1 下记录非默认格式,让无法识别该扩展的旧客户端停止操作,避免它们带着错误假设打开仓库。[5] 清晰的失败本身就是有用证据。只要某个受支持的工作站、构建镜像、IDE、程序库、备份代理或服务器打不开实验仓库,这次演练就已经在产生生产后果之前找到了阻塞项。
这项演练应与临时拼凑的转换流程严格分开。Git 文档说明,extensions.objectFormat 只能由 git init 或 git clone 设置;初始化后再编辑这一项,会让仓库进入难以诊断的状态。[3] GitLab 文档也记载了一种 SHA-256 项目创建选项,它位于默认关闭的 support_sha256_repositories 功能开关之后;文档将其限定为测试用实验功能,排除生产用途,并说明 Git 目前既无法把现有项目迁至 SHA-256,也无法迁回 SHA-1。[6] 因此,回滚应保持简单:删除金丝雀仓库,保留事实来源原貌。
40 个字符已经渗进外围系统
这次迁移最能暴露问题的代码,通常位于 Git 之外。对象 ID 会被复制进数据库列、缓存键、制品名称、URL、webhook schema、部署记录、发布清单、来源证明、聊天消息与事故工单。有些使用方会校验该值,有些会截断它,还有少数系统越过 40 字符的十六进制形式,直接保存原始 20-byte SHA-1 摘要。换用 SHA-256 后,这两个熟悉的宽度分别变为 32 bytes 和 64 个十六进制字符。[5]
先搜索源码与 schema。下面的模式刻意放得很宽,每一处匹配都需要人工判断:
rg -n --hidden \
'CHAR\(40\)|VARCHAR\(40\)|BINARY\(20\)|VARBINARY\(20\)|\{40\}|\b40\b' \
.
rg -n --hidden \
'sha1|commit[_-]?sha|commit[_-]?hash|object[_-]?id|git[_-]?oid' \
.
检查范围还要越过应用代码。SQL 迁移、protobuf 与 JSON schema、日志解析器、CI 模板、shell 参数切片、正则表达式、仪表盘字段、Terraform 状态、消息队列契约、fixture 和测试数据工厂都应纳入搜索。随后检查那些从未以源码形式出现的依赖:代码托管平台 SDK、Git 库、IDE 插件、准入控制器、发布机器人与安全扫描器。
把 CHAR(40) 扩成 CHAR(64) 只修补了局部问题。对于需要长期稳定的外部接口——这里谈的是应用 schema,与 Git 命令行语法分属不同层面——应把算法与完整的十六进制对象名称一同保存,例如 sha1:<40-hex> 或 sha256:<64-hex>;也可以采用带有 algorithm 和 hex 字段的类型化数据。算法限定符让解析器无须从长度推断含义,也为未来的名称转换服务留下足够信息,使它能够回答正确的问题。
缩写名称需要单独审计。7 个或 12 个字符的 commit 前缀只是显示上的便利写法,其唯一性取决于某个特定仓库当时包含哪些对象。这类前缀不适合充当数据库键、跨系统关联字段或永久发布证据。面向人类显示时可以让 Git 生成缩写;跨越机器边界时,则要传递完整且带算法限定符的名称。
Git 自身的迁移计划给出了一条值得借鉴的工程线索。准备任务之一,是用对象 ID 抽象和最大尺寸常量取代写死的原始尺寸与十六进制尺寸,也就是 20 和 40。[5] 下游系统也应采用同样的处理方式:把对象 ID 当作对象 ID 传递,别让字符串偶然形成的宽度升级为协议。
跟踪每一个标识符穿过所有交界处
一次显示绿色的 git status,只能证明 Git 核心程序读得懂一个仓库。它无法证明把 commit 变成经过审查、测试、发布、部署并且能够恢复的制品时,整条链路都能正常工作。演练要逐一覆盖这些交界:
- 从开发者到代码托管平台。 创建分支,执行 push 与 fetch,发起并合并一项变更,创建附注标签,再检查每一个暴露对象名称的 URL 与 API 响应。
- 从代码托管平台到自动化系统。 触发 push、tag、merge-request 与 release webhook。确认完整名称进入队列 payload、CI 环境变量、状态 API、部署服务和通知时,没有遭到拒绝或截断。
- 从 runner 到工具链。 测试每一个受支持的 Git 二进制程序与程序库,覆盖范围要超出维护者笔记本上的最新 CLI。语言绑定、源码生成器、变更日志工具、版本信息嵌入工具、扫描器,以及任何直接遍历
.git/objects的程序,都应包含在内。 - 从历史到制品。 构建软件包与容器,发布校验和与软件物料清单,附上来源证明,再沿审计时使用的同一组通道追查所记录的对象名称。
- 从服务到恢复。 备份仓库及其代码托管平台元数据,在空环境中恢复两者,运行
git fsck --full,然后重新完成克隆与构建。只复制裸仓库,而缺少 issue、merge request、受保护引用设置、CI 变量或发布元数据,仍算不上完整恢复测试。
每一处接缝都应捕获精确 payload。通过测试的界面也会遮住遭截断的数据库字段;成功的构建也会掩盖绑定到错误标识符的来源证明;webhook 使用方丢弃陌生值后,照样可以返回 HTTP 200。验收证据应包含生成的值、存储的值、发出的值,以及后来实际解析出的值。
兼容性矩阵还必须覆盖不那么显眼的仓库形态。金丝雀仓库里应放入一个合并提交、附注标签与签名标签、Git notes、一个子模块;组织使用大文件时,还要放入大文件指针,并加入有代表性的分支和标签数量。凡属受支持链路的功能,都要测试浅克隆、部分克隆、bundle、镜像、worktree、垃圾回收、归档生成与备份恢复。Git 的迁移设计特别指出,浅历史、子模块、alternate 对象库、notes、签名与服务端转换成本都需要特殊处理;这些警告应转化成测试用例,留在脚注里还不够。[5]
把旧名称保存为记录,别让它们沦为残片
既有 SHA-1 对象名称会出现在漏洞公告、Fixes: trailer、发布公告、issue 评论、邮件列表 patch 或客户支持案例中。这些引用记录着某个人在特定时刻所指的内容。若迁移后再也无法解析这些名称,那么即使所有源文件都完好无损,历史记录也已经受损。
Git 设想的互操作模型,会在仓库内部保存 SHA-1 与 SHA-256 名称之间的双向映射。它的分阶段设计还允许输入格式与输出格式在迁移的不同节点上改变。[5] 然而,当前实现的界线至关重要:compatObjectFormat 仍未完成,也尚未成为最终用户的迁移开关。[3] 今天的切换方案应排除对这项转换功能的依赖,因为当前手册仍将它限定为开发用途。
眼下可以先盘点外部引用。记录哪些系统保存完整 ID,哪些只保存缩写,以及哪些系统可以在保留旧值的同时加上算法限定符。等生产级名称转换功能到来后,应把映射作为持久数据保存,并附带所属仓库的身份与验证状态。一份写着 sha1:abc… 的 bug report 应继续保留这段文字;解析器可以补充对应的 SHA-256 名称,但不得悄悄改写历史引用。
签名也需要同样克制地处理。Git 的迁移设计预计 commit 与 tag 将有 SHA-1 和 SHA-256 两类签名字段,中间还会经历对象同时携带两者的时期。设计文档也指出,旧验证器会把只有 SHA-256 签名的 commit 视为未签名。[5] 因此,git verify-commit 与 git verify-tag 只是第一轮检查。演练还必须检查代码托管平台的“verified”状态、CI 签名策略、发布证明验证器、密钥发现链路,以及审计系统实际保留的证据。不得只为让仪表盘变绿就重新签署旧 release;应先明确策略对每个历史对象的要求究竟是保留、映射,还是新附一份证明。
分四个阶段推进,并为每一阶段设定明确停止条件
阶段 1:静态盘点。 找出固定宽度、未标注类型的对象名称字段、被用作键的缩写,以及直接读取 .git 的程序。记录每一项的负责团队与受支持版本。只有每一个权威数据存储与集成都有明确负责人,才能结束这一阶段;尚有多少修复项未完成,不影响这项退出条件。
阶段 2:本地 SHA-256 实验室。 针对一个用 --object-format=sha256 创建的临时仓库,运行组织日常使用的开发、测试、打包、签名与恢复命令。只有所有失败都能复现并完成分类,才能结束这一阶段。“能在平台团队的笔记本上运行”不足以作为退出条件。
阶段 3:代码托管平台金丝雀项目。 仅在代码托管平台明确开放 SHA-256 测试的条件下,使用非生产项目。合成接近真实情况的历史,走完上文所有交界,同时切断它与生产仓库的依赖。在已开启实验功能开关的 GitLab 实例上,SHA-256 创建流程可以成为一种实验室方案;它无法证明其他代码托管平台或集成也已就绪。[6] 只有销毁金丝雀项目时不会丢失任何正式成果,且每一个受支持客户端都有记录在案的结果,才能结束这一阶段。
阶段 4:等待完整的迁移契约。 一份生产迁移提案需要受支持的创建或转换途径、清晰的互操作方案、持久的旧名称到新名称解析、经过验证的备份与恢复,以及客户端输出另一种算法时的处置说明。关于 Git 这项工作的独立报道反复指出,整个工具体系的难题在于互操作性,焦点早已越过本地 SHA-256 对象存储。[7] 在这份契约正式发布并通过测试以前,仓库应保持“准备完毕,尚未转换”的状态。
团队规模改变的是矩阵深度,迁移界线保持不变。一个两人项目若只使用同一个最新版 Git 二进制程序和文件系统远端,很快就能完成一场有用的演练。一个通过多套 runner、SDK、镜像、签名系统与合规导出服务数百个仓库的平台,则需要在每一处交界安排负责人并留存证据。只要任何必要的生产链路仍有失败,就应让正式仓库留在实验之外。对于客户端与远端全部由团队掌控的全新仓库,采用 SHA-256 作为金丝雀项目是合理选择;这仍无法证明共享工具链已经就绪。
反证条件很直接:只要必要的生产方或权威使用方无法保存完整对象 ID 及明确无歧义的算法信息,某个使用方会截断或拒绝该值,或者受支持的代码托管平台缺少必要的互操作与恢复途径,生产迁移就还没有准备好。未来的默认值可以开启讨论,却代替不了一套测试通过的系统。
因此,2026 年最有价值的 SHA-256 准备工作,应从清除 Git 周边系统里一项隐蔽的 API 假设开始,再谈历史转换:对象 ID 永远只有 40 个字符。
来源
- Git 项目,“BreakingChanges”,Git 2.55.0 快照——未来大版本的默认值规划、SHA-1 风险背景、继续支持 SHA-1 的安排,以及 library、application 与 forge 必须为 SHA-256 做好准备的要求。
- Git 项目,
git-init文档,Git 2.55.0 快照——--object-format、当前默认使用 SHA-1,以及 SHA-1 与 SHA-256 仓库目前无法互操作。 - Git 项目,
git-config文档,Git 2.55.0 快照——extensions.objectFormat、初始化后不得更改该值的警告,以及尚未完成的compatObjectFormat支持仅供开发使用。 - Git 项目,
git-rev-parse文档,Git 2.55.0 快照——仓库对象格式查询,以及对象名称的输入与输出控制。 - Git 项目,“Hash Function Transition”技术设计,Git 2.55.0 快照——对象序列化、双名称映射、签名、迁移模式、注意事项与实现阶段。
- GitLab 文档,“Create a project that uses SHA-256 hashing”——实验性测试功能、创建时选择,以及当前两个方向都没有迁移功能。
- Jonathan Corbet,“Git considers SHA-256, Rust, LLMs, and more”,LWN.net,2025 年 10 月 21 日——关于互操作工作与生态准备情况的独立报道。
- Lee Reilly,“Git Merge 2022 — that's a wrap!”,GitHub Blog,2022 年 10 月 21 日——活动报道,也是本文所用会议档案照片的来源页面。
- Taylor Blau,“Highlights from Git 2.29”,GitHub Blog,2020 年 10 月 19 日——Git 首次提供实验性 SHA-256 仓库支持时的同期记录,以及当时的互操作界线。