简体中文 ▾ 主题 ▾ 最新版本 ▾ git-pack-objects 最新更新于 2.55.0

名称

git-pack-objects - 创建对象打包归档

概要

git pack-objects [-q | --progress | --all-progress] [--all-progress-implied]
		   [--no-reuse-delta] [--delta-base-offset] [--non-empty]
		   [--local] [--incremental] [--window=<n>] [--depth=<n>]
		   [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
		   [--cruft] [--cruft-expiration=<time>]
		   [--stdout [--filter=<filter-spec>] | <base-name>]
		   [--shallow] [--keep-true-parents] [--[no-]sparse]
		   [--name-hash-version=<n>] [--path-walk] < <object-list>

描述

从标准输入读取对象列表,然后将一个或多个具有指定 base-name(基本名称)的打包归档写入磁盘,或者将一个打包归档写入标准输出。

打包归档是在两个仓库之间传输一组对象的高效方式,也是一种高效访问的归档格式。在打包归档中,对象要么以压缩后的整体形式存储,要么以与其他对象的差异形式存储。后者通常被称为增量(delta)。

打包归档格式(.pack)被设计为自包含的,以便在不需要任何额外信息的情况下进行解包。因此,增量所依赖的每个对象都必须存在于该包中。

会生成一个包索引文件(.idx)用于对包中的对象进行快速随机访问。将索引文件(.idx)和打包归档(.pack)同时放入 $GIT_OBJECT_DIRECTORY(或 $GIT_ALTERNATE_OBJECT_DIRECTORIES 中的任何目录)的 pack/ 子目录下,即可使 Git 能够从该包归档中进行读取。

git unpack-objects 命令可以读取打包归档,并将包中包含的对象展开为“一文件一对象”的格式;这通常由智能拉取(smart-pull)命令在为了对端间的高效网络传输而即时创建包时执行。

选项

base-name

写入成对的文件(.pack 和 .idx),使用 <base-name> 确定所创建文件的名称。使用此选项时,成对的两个文件将写入 <base-name>-<SHA-1>.{pack,idx} 文件中。<SHA-1> 是基于包内容计算的哈希值,会被写入到该命令的标准输出。

--stdout

将包内容(即原本会写入 .pack 文件的内容)写入到标准输出。

--revs

从标准输入读取版本(revision)参数,而不是单个对象名称。这些版本参数的处理方式与 git rev-list 配合 --objects 标志使用其 commit 参数来构建其输出对象列表的方式相同。结果列表中的对象会被打包。除了版本之外,还接受 --not--shallow <SHA-1> 行。

--unpacked

这会隐式启用 --revs。在处理从标准输入读取的版本参数列表时,将打包的对象限制为那些尚未打包的对象。

--all

这会隐式启用 --revs。除了从标准输入读取的版本参数列表外,还会假装 refs/ 下的所有引用都被指定包含在内。

--include-tag

如果未请求的附注标签(annotated tag)所引用的对象已包含在生成的包文件中,则也将这些标签包含进来。这对于将新标签发送给原生 Git 客户端很有用。

--stdin-packs[=<mode>]

从标准输入读取包文件的基本名称(例如 pack-1234abcd.pack),而不是对象名称或版本参数。生成的包将包含被包含包(不以 ^ 开头)中列出的所有对象,并排除被排除包(以 ^ 开头)中列出的任何对象。

mode 为 "follow" 时,包的前缀可能还会带有 !,表示它们被排除在外,但在可达性下不一定闭合。除了包含包中的对象外,生成的包还可能根据以下内容包含其他对象

  • 如果有任何包被标记为 !,则通过排除闭合(excluded-closed)包之外的对象,从这些包或被包含包可达的对象也将被包含在内。在这种情况下,所有 ^ 包在可达性下都被视为闭合的。

  • 否则(如果没有 ! 包),如果未列出包中的对象满足以下条件,则它们将被包含在内:(1) 可从被包含包到达,且 (2) 在任何被排除包中均未找到。

此模式很有用,例如,可以恢复在废弃包(cruft pack)中发现的曾经不可达的对象,以生成在可达性下闭合直至被排除包所设定边界的包。

--revs 或隐式启用 --revs 的选项(例如 --all)不兼容,但 --unpacked 除外,它是兼容的。

--cruft

将不可达的对象打包到一个单独的“废弃”(cruft)包中,该包通过存在 .mtimes 文件来标识。通常由 git repack --cruft 使用。调用者提供包名称列表,并指出哪些包将保留在仓库中,以及哪些包将被删除(由 - 前缀表示)。废弃包的内容是所有未包含在存活包中、且未超过宽限期(参见下文的 --cruft-expiration),或者虽然已超过宽限期但可从另一个未超过宽限期的对象到达的对象。

当输入列表包含一个拥有所有可达对象的包(并将所有其他包列为待删除)时,相应的废弃包将包含所有不可达对象(其 mtime 新于 --cruft-expiration),以及任何 mtime 虽早于 --cruft-expiration 但可从一个 mtime 新于 --cruft-expiration 的不可达对象到达的不可达对象。

--unpack-unreachable--keep-unreachable--pack-loose-unreachable--stdin-packs,以及任何其他隐式启用 --revs 的选项不兼容。

--cruft-expiration=<approxidate>

如果指定,若对象的 mtime 早于 <approxidate>,则会从废弃包中剔除。如果未指定(且给定了 --cruft),则不会剔除任何对象。

--window=<n>
--depth=<n>

这两个选项会影响包中包含的对象在使用增量压缩时的存储方式。对象首先在内部按类型、大小和(可选的)名称进行排序,并与 --window 内的其他对象进行比较,以查看使用增量压缩是否可以节省空间。--depth 限制了最大增量深度;设置得太深会影响解包端的性能,因为需要多次应用增量数据才能获取到所需的对象。

--window 的默认值为 10,--depth 的默认值为 50。最大深度为 4095。

--window-memory=<n>

此选项在 --window 的基础上提供了一个额外的限制;窗口大小会动态缩小,以便在内存中占用不超过 <n> 字节。在同时混合了大对象和小对象的仓库中,这非常有用,可以避免在使用大窗口时耗尽内存,同时仍能让小对象利用大窗口的优势。该大小可以使用后缀 “k”、“m” 或 “g”。--window-memory=0 表示不限制内存使用。默认值取自 pack.windowMemory 配置变量。

--max-pack-size=<n>

在一些特殊场景中,你可能无法在文件系统上创建大于特定大小的文件,此时可以使用此选项指示命令将输出的包文件拆分为多个独立的包文件,每个包文件的大小都不大于给定值。该大小可以使用后缀 “k”、“m” 或 “g”。允许的最小大小限制为 1 MiB。默认是无限制,除非设置了 pack.packSizeLimit 配置变量。请注意,此选项可能会导致仓库变大且变慢;请参阅对 pack.packSizeLimit 的讨论。

--honor-pack-keep

此标志使已经在含有 .keep 文件的本地包中的对象被忽略,即使它原本应该被打包。

--keep-pack=<pack-name>

此标志使已经在给定包中的对象被忽略,即使它原本应该被打包。<pack-name> 是不含前导目录的包文件名(例如 pack-123.pack)。可以多次指定此选项以保留多个包。

--incremental

此标志使已经在某个包中的对象被忽略,即使它原本应该被打包。

--local

此标志使从备用对象库(alternate object store)借用的对象被忽略,即使它原本应该被打包。

--non-empty

仅在打包归档将包含至少一个对象时才进行创建。

--progress

默认情况下,当标准错误流连接到终端时,会报告进度状态,除非指定了 -q。此标志强制报告进度状态,即使标准错误流未定向到终端。

--all-progress

当指定 --stdout 时,在对象计数和压缩阶段会显示进度报告,但在写入阶段会被禁用。其原因在于,在某些情况下,输出流直接连接到另一个命令,该命令可能希望在处理传入的包数据时显示其自己的进度状态。此标志与 --progress 类似,不同之处在于即使使用了 --stdout,它也会强制显示写入阶段的进度报告。

--all-progress-implied

此标志用于在激活进度显示时隐式启用 --all-progress。与 --all-progress 不同,该标志本身实际上不会强制进行任何进度显示。

-q

此标志使命令不在标准错误流上报告其进度。

--no-reuse-delta

在已有包的仓库中创建打包归档时,该命令会复用现有的增量(delta)。这有时会导致生成的包稍微不够优化。此标志指示命令不复用现有的增量,而是从头开始重新计算它们。

--no-reuse-object

此标志指示命令完全不复用任何现有的对象数据(包括未进行增量处理的对象),从而强制对所有内容进行重新压缩。这会隐式启用 --no-reuse-delta。这仅在需要对打包数据全面强制执行不同压缩级别的罕见情况下有用。

--compression=<n>

指定生成的包中新压缩数据的压缩级别。如果未指定,包压缩级别将首先由 pack.compression 决定,其次由 core.compression 决定;如果两者均未设置,则默认为 -1(zlib 的默认值)。如果你想强制对所有数据(无论来源如何)采用统一的压缩级别,请添加 --no-reuse-object。

--sparse
--no-sparse

与 "--revs" 选项配合使用时,切换用于确定哪些对象应包含在包中的“稀疏(sparse)”算法。该算法仅遍历引入新对象的路径中出现的树。在计算用于发送较小变更的包时,这可以带来显著的性能提升。然而,如果所包含的提交中含有特定类型的直接重命名,则可能会向包文件中添加额外的对象。如果不提供此选项,它将默认使用 pack.useSparse 的值,除非另有指定,否则该值默认为 true。

--thin

通过忽略发送方和接收方之间的公共对象,创建一个“瘦”(thin)包,以减少网络传输。此选项仅在与 --stdout 结合使用时才有意义。

注意:瘦包由于省略了所需的某些对象而违反了打包归档格式,因此在使其自包含之前无法由 Git 直接使用。使用 git index-pack --fix-thin(参见 git-index-pack[1])来恢复自包含属性。

--shallow

对将提供给具有浅仓库(shallow repository)的客户端的包进行优化。此选项与 --thin 结合使用时,可以生成更小的包,但代价是降低速度。

--delta-base-offset

打包归档可以将增量的基对象表示为 20 字节的对象名称,或者表示为流中的偏移量,但旧版本的 Git 无法理解后者。默认情况下,为了获得更好的兼容性,git pack-objects 仅使用前一种格式。此选项允许命令使用后一种格式以达到紧凑性。根据平均增量链长度,此选项通常可以将生成的包文件缩小 3-5%。

注意:在现代 Git 中,诸如 git gc(参见 git-gc[1])、git repack(参见 git-repack[1])等上层命令(porcelain commands)在将仓库中的对象存入包文件时,默认会传递此选项。git bundle(参见 git-bundle[1])在创建归档包时也是如此。

--threads=<n>

指定搜索最佳增量匹配时要创建的线程数。这要求编译 pack-objects 时启用了 pthreads,否则此选项将被忽略并发出警告。这旨在减少多处理器机器上的打包时间。然而,增量搜索窗口所需的内存量将乘以线程数。指定为 0 将导致 Git 自动检测 CPU 的数量并相应地设置线程数。

--index-version=<version>[,<offset>]

这仅供测试套件使用。它允许强制指定生成的包索引的版本,并强制对位于给定偏移量以上的对象使用 64 位索引条目。

--keep-true-parents

使用此选项,即使父提交被嫁接(grafts)所隐藏,也仍会被打包。

--filter=<filter-spec>

从生成的包文件中省略某些对象(通常是二进制大对象,即 blobs)。有关有效的 <filter-spec> 形式,请参见 git-rev-list[1]

--no-filter

关闭之前指定的任何 --filter= 参数。

--missing=<missing-action>

一个用于辅助未来“部分克隆(partial clone)”开发的调试选项。此选项指定如何处理缺失的对象。

--missing=error 形式要求在遇到缺失对象时,pack-objects 停止并报错。如果该仓库是部分克隆,则在宣布对象缺失之前,会尝试获取(fetch)这些缺失的对象。这是默认行为。

--missing=allow-any 形式将允许在遇到缺失对象时继续进行对象遍历。不会发生对缺失对象的获取。缺失的对象将被静默地从结果中省略。

--missing=allow-promisor 形式与 allow-any 类似,但仅允许对“预期的” promisor 缺失对象继续进行对象遍历。不会发生对缺失对象的获取。如果出现非预期的缺失对象,则会引发错误。

--exclude-promisor-objects

省略已知存在于 promisor 远程仓库中的对象。(此选项的目的是仅对本地创建的对象进行操作,以便在重新打包时,我们仍能保持本地创建的对象 [无 .promisor] 与来自 promisor 远程仓库的对象 [有 .promisor] 之间的区别。)这与部分克隆结合使用。

--keep-unreachable

除了不在标记有 *.keep 文件的包中的可达对象外,使用 --unpacked= 选项命名的包中引用不可达的对象也会被添加到生成的包中。这会隐式启用 --revs

--pack-loose-unreachable

打包不可达的松散对象(并移除其松散的对应物)。这会隐式启用 --revs

--unpack-unreachable

以松散形式保留不可达的对象。这会隐式启用 --revs

--delta-islands

基于“岛(islands)”限制增量匹配。参见下文的增量岛(DELTA ISLANDS)。

--name-hash-version=<n>

在执行增量压缩时,Git 会根据基于该对象路径的启发式算法,对可能相似的对象进行分组。虽然通过精确路径匹配对对象进行分组对于具有多个版本的路径非常有利,但在不同的完整路径之间寻找增量对也大有裨益。Git 按类型、然后按路径的“名称哈希(name hash)”、再按大小收集对象,希望能将可以良好压缩在一起的对象归为一组。

默认的名称哈希版本是 1,它通过将路径的最后几个字节视为对哈希函数提供最大幅度的权重,从而优先考虑哈希局部性。此版本在区分短路径和跨目录查找重命名方面表现优异。然而,该哈希函数主要取决于路径的最后 16 个字节。如果仓库中有很多路径具有相同的最后 16 字节、且仅在父目录上有所不同,则此名称哈希可能会导致过多的冲突并导致效果不佳。目前,在使用 --write-bitmap-index 写入可达性位图文件时,必须使用此版本。

名称哈希版本 2 具有与版本 1 类似的局部性特征,只是它会单独考虑每个路径组件,并通过移位叠加哈希值。这仍然优先考虑路径的最后几个字节,但也会使用父目录名称对哈希的低位进行“加盐(salt)”。此方法在保留版本 1 的部分局部性优势的同时,打破了因出现在许多不同目录下的同名文件而引起的大多数冲突。目前,在使用 --write-bitmap-index 写入可达性位图文件时不允许使用此版本,它会被自动更改为版本 1

--path-walk

首先通过按路径组织对象来进行压缩,然后进行第二轮按常规跨路径压缩。这有可能改善增量压缩,特别是在存在会导致 Git 默认名称哈希算法产生冲突的文件名时。

--delta-islands 不兼容。在存在 --path-walk 时,--use-bitmap-index 选项会被忽略。--path-walk 选项支持以下 --filter=<spec> 形式:blob:noneblob:limit=<n>tree:0object:type=<type>sparse:<oid>。这些支持的过滤器类型可以与 combine:<spec>+<spec> 形式结合使用。

增量岛 (DELTA ISLANDS)

在可能的情况下,pack-objects 会尝试复用现有的磁盘增量,以避免必须即时搜索新的增量。这是服务于获取(fetch)操作的一项重要优化,因为它意味着服务器可以完全避免解压大多数对象,只需直接从磁盘发送字节。当一个对象作为相对于接收方没有的基对象(且我们尚未发送)的增量进行存储时,此优化将无法工作。在这种情况下,服务器会“打断(breaks)”增量并必须寻找一个新增量,这会带来高昂的 CPU 开销。因此,为了提高性能,磁盘上增量关系中的对象集合与客户端将获取的对象相匹配是非常重要的。

在一个普通的仓库中,这往往会自动起作用。对象大多数可从分支和标签到达,而这正是客户端获取的内容。我们在服务器上找到的任何增量,很可能是客户端已拥有或即将拥有的对象之间的增量。

但在某些仓库设置中,你可能会有几组相关但独立的引用顶端(ref tips),而客户端倾向于独立获取这些组。例如,假设你在单个共享对象库中托管一个仓库的多个“分叉(forks)”,并让客户端通过 GIT_NAMESPACE 将它们视为独立的仓库,或者使用备用(alternates)机制将其视为独立的仓库。幼稚的重新打包可能会发现,某个对象的最佳增量是相对于仅在另一个分叉中找到的基对象的。但当客户端进行获取时,他们将没有该基对象,我们不得不即时寻找一个新的增量。

如果你在 refs/heads/refs/tags/ 之外有许多指向相关对象的引用(例如,某些托管服务提供商使用的 refs/pullrefs/changes),也可能会存在类似的情况。默认情况下,客户端仅获取 heads 和 tags,因此无法原样发送针对仅存在于这些其他组中的对象的增量。

增量岛(Delta islands)通过允许你将引用分组成不同的“岛”来解决这个问题。Pack-objects 计算哪些对象可从哪些岛到达,并拒绝将对象 A 针对一个不存在于 A 的所有岛中的基对象进行增量化。这会导致生成的包稍微变大(因为我们错失了一些增量机会),但可以保证获取一个岛的内容时,不会因为跨越岛的边界而不得不即时重新计算增量。

在使用增量岛重新打包时,增量窗口往往会充斥着被配置禁止的候选对象。使用较大的 --window 重新打包会有所帮助(而且不会花费原本可能那么长的时间,因为在对内容进行任何计算之前,我们可以基于岛拒绝某些对象对)。

增量岛是通过 pack.island 选项配置的,该选项可以指定多次。每个值都是一个左锚定的匹配引用名的正则表达式。例如

[pack]
island = refs/heads/
island = refs/tags/

将 heads 和 tags 放入一个岛中(其名称为空字符串;关于命名的更多信息见下文)。任何不匹配这些正则表达式的引用(例如 refs/pull/123)都不在任何岛中。因此,仅能从 refs/pull/(而不能从 heads 或 tags)到达的任何对象,都不能作为 refs/heads/ 的基对象的候选。

引用会根据它们的“名称”被分组成岛,产生相同名称的两个正则表达式被视为在同一个岛中。名称是通过将正则表达式中的任何捕获组连接起来计算得出的,中间用破折号 - 隔开。(如果没有捕获组,则名称为空字符串,如上例所示。)这允许你创建任意数量 of 岛。但最多仅支持 14 个此类捕获组。

例如,假设你将每个分叉的引用存储在 refs/virtual/ID 中,其中 ID 是一个数字标识符。你随后可能会配置

[pack]
island = refs/virtual/([0-9]+)/heads/
island = refs/virtual/([0-9]+)/tags/
island = refs/virtual/([0-9]+)/(pull)/

这会将每个分叉的 heads 和 tags 放入它们自己的岛中(命名为“1234”或类似名称),每个分叉的 pull 引用则进入它们自己的“1234-pull”岛中。

请注意,我们为每个正则表达式选择一个唯一的岛,并采用“后者胜出(last one wins)”的顺序(这允许仓库专属的配置优先于全局配置等)。

配置

有多种配置变量会影响打包,请参见 git-config[1](搜索 “pack” 和 “delta”)。

值得注意的是,对于大于 core.bigFileThreshold 配置变量的对象,以及属性 delta 设置为 false 的文件,不会使用增量压缩。

GIT

Git[1] 套件的一部分