Git
英语 ▾ 主题 ▾ 最新版本 ▾ git-fast-import 最后更新于 2.45.0

名称

git-fast-import - 快速 Git 数据导入器的后端

概要

frontend | git fast-import [<options>]

描述

此程序通常不是最终用户想要直接运行的程序。大多数最终用户想要使用现有的前端程序之一,这些程序解析特定类型的外部源并将存储在那里的内容提供给 git fast-import

fast-import 从标准输入读取混合的命令/数据流,并将一个或多个 packfile 直接写入当前仓库。当标准输入收到 EOF 时,fast-import 会写入更新的分支和标签引用,从而使用新导入的数据完全更新当前仓库。

fast-import 后端本身可以导入到空仓库(由 git init 初始化的仓库)中,也可以增量更新现有的填充仓库。是否支持从特定外部源进行增量导入取决于正在使用的前端程序。

选项

--force

强制更新修改过的现有分支,即使这样做会导致提交丢失(因为新提交不包含旧提交)。

--quiet

禁用 --stats 显示的输出,使 fast-import 在成功时通常保持静默。但是,如果导入流包含旨在显示用户输出的指令(例如 progress 指令),则相应的消息仍将显示。

--stats

显示有关 fast-import 创建的对象、存储它们的 packfile 以及 fast-import 在此运行期间使用的内存的一些基本统计信息。目前,显示此输出是默认行为,但可以使用 --quiet 禁用。

--allow-unsafe-features

可以使用 featureoption 命令在 fast-import 流本身中提供许多命令行选项。但是,其中一些选项是不安全的(例如,允许 fast-import 访问仓库外部的文件系统)。这些选项默认情况下处于禁用状态,但可以通过在命令行中提供此选项来允许。目前,这仅影响 export-marksimport-marksimport-marks-if-exists 特性命令。

Only enable this option if you trust the program generating the
fast-import stream! This option is enabled automatically for
remote-helpers that use the `import` capability, as they are
already trusted to run their own code.

前端选项

--cat-blob-fd=<fd>

将对 get-markcat-blobls 查询的响应写入文件描述符 <fd> 而不是 stdout。允许将旨在供最终用户使用的 progress 输出与其他输出分离。

--date-format=<fmt>

指定前端将在 authorcommittertagger 命令中提供给 fast-import 的日期类型。有关支持的格式及其语法的详细信息,请参阅下面的“日期格式”。

--done

如果流末尾没有 done 命令,则终止并出现错误。此选项可能有助于检测导致前端在开始写入流之前就终止的错误。

标记文件的位置

--export-marks=<file>

完成后将内部标记表转储到 <file>。标记以每行一个 :markid SHA-1 的格式写入。前端可以使用此文件在导入完成后验证导入,或者在增量运行之间保存标记表。由于 <file> 仅在检查点(或完成时)打开并截断,因此相同的路径也可以安全地提供给 --import-marks。

--import-marks=<file>

在处理任何输入之前,加载 <file> 中指定的标记。输入文件必须存在,必须可读,并且必须使用与 --export-marks 生成的格式相同的格式。可以提供多个选项以导入多个标记集。如果一个标记被定义为不同的值,则最后一个文件获胜。

--import-marks-if-exists=<file>

与 --import-marks 相似,但如果文件不存在,则不会报错,而是静默跳过文件。

--[no-]relative-marks

在指定 --relative-marks 之后,使用 --import-marks= 和 --export-marks= 指定的路径相对于当前仓库中的内部目录。在 git-fast-import 中,这意味着路径相对于 .git/info/fast-import 目录。但是,其他导入器可能使用不同的位置。

可以通过将 --(no-)-relative-marks 与 --(import|export)-marks= 选项交织在一起来组合相对标记和非相对标记。

子模块重写

--rewrite-submodules-from=<name>:<file>
--rewrite-submodules-to=<name>:<file>

将由 <name> 指定的子模块的对象 ID 从 from <file> 中使用的值重写为 to <file> 中使用的值。from 标记应该由 git fast-export 创建,而 to 标记应该在导入同一个子模块时由 git fast-import 创建。

<name> 可以是任何不包含冒号字符的任意字符串,但为两个选项指定对应标记时必须使用相同的值。可以使用 <name> 的不同值指定多个子模块。如果没有成对使用这些选项,则会报错。

当将仓库从一种哈希算法转换为另一种算法时,这些选项主要有用;如果没有这些选项,如果 fast-import 遇到子模块,它将失败,因为它没有办法将对象 ID 写入新的哈希算法。

性能和压缩调整

--active-branches=<n>

一次维护活动的最多分支数。有关详细信息,请参阅下面的“内存使用情况”。默认值为 5。

--big-file-threshold=<n>

fast-import 将尝试为其创建增量的最大 blob 大小(以字节为单位)。默认值为 512m(512 MiB)。某些导入器可能希望在内存受限的系统上降低此值。

--depth=<n>

最大增量深度,用于 blob 和 tree 增量。默认值为 50。

--export-pack-edges=<file>

创建 packfile 后,将一行数据打印到 <file>,其中包含 packfile 的文件名以及写入该 packfile 的每个分支上的最后一次提交。在导入总对象集超过 4 GiB packfile 限制的项目后,此信息可能很有用,因为这些提交可作为调用 git pack-objects 时的边缘点。

--max-pack-size=<n>

每个输出 packfile 的最大大小。默认情况下无限制。

fastimport.unpackLimit

参见 git-config[1]

性能

fast-import 的设计允许它在最少的内存使用量和处理时间内导入大型项目。假设前端能够跟上 fast-import 并向其提供恒定的数据流,那么对于包含 10 多年历史记录并包含 100,000 多个独立提交的项目,导入时间通常只需 1-2 个小时,这在相当普通的(约 2,000 美元)硬件上即可完成。

大多数瓶颈似乎出现在外部源数据访问(源无法以足够快的速度提取修订版)或磁盘 IO(fast-import 写入速度与磁盘接受数据的速度一样快)。如果源数据存储在与目标 Git 存储库不同的驱动器上,导入速度会更快(因为 IO 竞争减少)。

开发成本

fast-import 的典型前端通常需要大约 200 行 Perl/Python/Ruby 代码。大多数开发人员在短短几个小时内就能够创建出可用的导入程序,即使他们之前从未接触过 fast-import,甚至从未接触过 Git。这是一个理想的情况,因为大多数转换工具都是一次性使用的(使用一次,永远不会再用)。

并行操作

git pushgit fetch 一样,由 fast-import 处理的导入可以安全地与并行的 git repack -a -dgit gc 调用或任何其他 Git 操作(包括 git prune,因为 fast-import 永远不会使用松散对象)一起运行。

fast-import 不会锁定它正在积极导入的分支或标签引用。导入完成后,在它的引用更新阶段,fast-import 会测试每个现有的分支引用,以验证更新将是快进更新(引用中存储的提交包含在要写入的提交的新历史记录中)。如果更新不是快进更新,fast-import 将跳过更新该引用,而是打印一条警告消息。fast-import 将始终尝试更新所有分支引用,并且不会在第一次失败时停止。

可以使用 --force 强制更新分支,但建议仅在其他情况下安静的存储库中使用。对于对空存储库的初始导入,不需要使用 --force。

技术讨论

fast-import 在内存中跟踪一组分支。在导入过程中的任何时候,都可以通过在输入流上发送 commit 命令来创建或修改任何分支。这种设计允许前端程序同时处理无限数量的分支,并按从源数据中获得的顺序生成提交。它还简化了前端程序。

fast-import 不会使用或更改当前工作目录或其中的任何文件。(它确实更新了当前的 Git 存储库,如 GIT_DIR 所引用)。因此,导入前端可以使用工作目录来完成自己的目的,例如从外部源中提取文件修订版。这种对工作目录的忽视也允许 fast-import 运行得非常快,因为它不需要在分支之间切换时执行任何代价高昂的文件更新操作。

输入格式

除了原始文件数据(Git 不会解释)之外,fast-import 输入格式是基于文本(ASCII)的。这种基于文本的格式简化了前端程序的开发和调试,尤其是在使用 Perl、Python 或 Ruby 等高级语言时。

fast-import 对其输入非常严格。在下面我们说 SP 时,是指**正好**一个空格。同样,LF 指一个(且仅一个)换行符,HT 指一个(且仅一个)水平制表符。提供额外的空格字符会导致意外的结果,例如分支名称或文件名在其名称中包含前导或尾随空格,或者当遇到意外输入时,fast-import 会提前终止。

流注释

为了帮助调试前端,fast-import 会忽略任何以 #(ASCII 磅/哈希)开头的行,直到且包括行结束符 LF。注释行可以包含任何不包含 LF 的字节序列,因此可以用来包含任何可能特定于前端且在检查 fast-import 数据流时有用的详细调试信息。

日期格式

支持以下日期格式。前端应该通过在 --date-format=<fmt> 命令行选项中传递格式名称来选择将为此导入使用的格式。

raw

这是 Git 本机格式,为 <time> SP <offutc>。它也是 fast-import 的默认格式,如果没有指定 --date-format。

事件的时间由 <time> 指定,为自 UNIX 纪元(1970 年 1 月 1 日午夜,UTC)以来的秒数,并以 ASCII 十进制整数形式写入。

本地偏移量由 <offutc> 指定,为相对于 UTC 的正或负偏移量。例如,EST(比 UTC 落后 5 小时)在 <tz> 中表示为“-0500”,而 UTC 为“+0000”。本地偏移量不会影响 <time>;它仅用作建议,以帮助格式化例程显示时间戳。

如果源材料中没有本地偏移量,请使用“+0000”,或最常见的本地偏移量。例如,许多组织都有一个 CVS 存储库,该存储库只被位于同一地点和时区的用户访问。在这种情况下,可以假设一个合理的 UTC 偏移量。

rfc2822 格式不同,此格式非常严格。任何格式上的变化都会导致 fast-import 拒绝该值,并且还会执行一些对数值的健全性检查。

raw-permissive

这与 raw 相同,只是不对数字纪元和本地偏移量执行健全性检查。当尝试过滤或导入具有例如错误时区值的现有历史记录时,这可能很有用。

rfc2822

这是 RFC 2822 中描述的标准日期格式。

一个示例值为“Tue Feb 6 11:22:18 2007 -0500”。Git 解析器很精确,但有点宽松。它与 git am 在应用从电子邮件收到的补丁时使用的解析器相同。

一些格式错误的字符串可能会被接受为有效日期。在其中一些情况下,Git 仍然能够从格式错误的字符串中获取正确的日期。还有一些类型的格式错误的字符串,Git 会解析错误,但仍然认为有效。严重格式错误的字符串将被拒绝。

与上面的 raw 格式不同,RFC 2822 日期字符串中包含的时区/UTC 偏移量信息用于在存储之前将日期值调整为 UTC。因此,此信息尽可能准确非常重要。

如果源材料使用 RFC 2822 风格的日期,前端应该让 fast-import 处理解析和转换(而不是尝试自己完成),因为 Git 解析器已经过实际应用的充分测试。

如果源材料已经使用 UNIX 纪元格式,可以被哄骗为使用该格式,或者其格式可以轻松转换为该格式,前端应该首选 raw 格式,因为解析没有歧义。

now

始终使用当前时间和时区。对于 <when>,必须始终提供字面量 now

这是一个玩具格式。此系统的当前时间和时区始终在 fast-import 创建标识字符串时被复制到标识字符串中。无法指定不同的时间或时区。

提供这种特殊的格式是因为它实现起来很短,并且可能对想要立即创建新提交的过程有用,而不需要使用工作目录或 git update-index

如果在 commit 中使用单独的 authorcommitter 命令,则时间戳可能不匹配,因为系统时钟将被轮询两次(每个命令一次)。确保作者和提交者标识信息具有相同时间戳的唯一方法是省略 author(因此从 committer 复制)或使用除 now 以外的日期格式。

命令

fast-import 接受多个命令来更新当前存储库并控制当前导入过程。每个命令的更详细讨论(包括示例)将在后面介绍。

commit

通过创建新提交并更新分支以指向新创建的提交,来创建新分支或更新现有分支。

tag

从现有提交或分支创建带注释的标签对象。此命令不支持轻量级标签,因为它们不推荐用于记录有意义的时间点。

reset

将现有分支(或新分支)重置为特定修订版。必须使用此命令将分支更改为特定修订版,而不会在其上进行提交。

blob

将原始文件数据转换为 blob,以便将来在 commit 命令中使用。此命令是可选的,不需要执行导入。

alias

记录标记引用给定对象,而无需首先创建任何新对象。使用 --import-marks 并引用缺少的标记会导致 fast-import 失败,因此别名可以提供一种方法将否则会被修剪的提交设置为有效值(例如,最近的未修剪祖先)。

checkpoint

强制 fast-import 关闭当前 packfile,生成其唯一的 SHA-1 校验和和索引,并开始一个新的 packfile。此命令是可选的,不需要执行导入。

progress

导致 fast-import 将整行回显到其自身的标准输出。此命令是可选的,不需要执行导入。

done

标记流的结束。此命令是可选的,除非使用 --done 命令行选项或 feature done 命令请求 done 功能。

get-mark

导致 fast-import 将与标记对应的 SHA-1 打印到使用 --cat-blob-fd 设置的文件描述符集,或者未指定则打印到 stdout

cat-blob

导致 fast-import 以 cat-file --batch 格式将 blob 打印到使用 --cat-blob-fd 设置的文件描述符集,或者未指定则打印到 stdout

ls

导致 fast-import 将以 ls-tree 格式描述目录条目的行打印到使用 --cat-blob-fd 设置的文件描述符集,或者未指定则打印到 stdout

feature

启用指定的功能。这需要 fast-import 支持指定的功能,如果它不支持,则会中止。

option

指定 OPTIONS 下列出的任何不更改流语义以适应前端需求的选项。此命令是可选的,执行导入不需要它。

commit

创建或更新分支并使用新提交记录对项目的逻辑更改。

	'commit' SP <ref> LF
	mark?
	original-oid?
	('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
	'committer' (SP <name>)? SP LT <email> GT SP <when> LF
	('encoding' SP <encoding>)?
	data
	('from' SP <commit-ish> LF)?
	('merge' SP <commit-ish> LF)*
	(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
	LF?

其中 <ref> 是要提交分支的名称。通常,分支名称在 Git 中以 refs/heads/ 为前缀,因此导入 CVS 分支符号 RELENG-1_0 将使用 refs/heads/RELENG-1_0 作为 <ref> 的值。<ref> 的值必须是 Git 中有效的 refname。由于 LF 在 Git refname 中无效,因此此处不支持引号或转义语法。

一个 mark 命令可以可选地出现,请求 fast-import 保存对新创建提交的引用,供前端将来使用(有关格式,请参见下文)。前端通常会标记他们创建的每个提交,从而允许从任何导入的提交创建未来的分支。

committer 之后的 data 命令必须提供提交消息(有关 data 命令语法,请参见下文)。要导入空提交消息,请使用长度为 0 的数据。提交消息是自由格式的,不受 Git 的解释。目前,它们必须使用 UTF-8 编码,因为 fast-import 不允许指定其他编码。

可以包含零个或多个 filemodifyfiledeletefilecopyfilerenamefiledeleteallnotemodify 命令,以更新分支的内容,然后再创建提交。这些命令可以按任何顺序提供。但是,建议在同一提交中的所有 filemodifyfilecopyfilerenamenotemodify 命令之前提供 filedeleteall 命令,因为 filedeleteall 会将分支清理干净(见下文)。

命令后的 LF 是可选的(以前是必需的)。请注意,出于向后兼容性的原因,如果提交以 data 命令结尾(即它没有 frommergefilemodifyfiledeletefilecopyfilerenamefiledeleteallnotemodify 命令),则命令末尾可能出现两个 LF 命令,而不是一个。

author

如果作者信息可能与提交者信息不同,则可以可选地出现 author 命令。如果省略了 author,则 fast-import 将自动使用提交者的信息作为提交的作者部分。有关 author 中字段的说明,请参见下文,因为它们与 committer 相同。

committer

committer 命令指示谁进行了此提交,以及他们何时提交。

这里 <name> 是该人的显示名称(例如“Com M Itter”),而 <email> 是该人的电子邮件地址(“[email protected]”)。LTGT 是文字小于号(\x3c)和大于号(\x3e)符号。这些是必需的,用于将电子邮件地址与行中的其他字段隔开。请注意,<name><email> 是自由格式的,可以包含任何字节序列,但 LTGTLF 除外。<name> 通常是 UTF-8 编码的。

更改时间由 <when> 使用 --date-format=<fmt> 命令行选项选择的日期格式指定。有关支持的格式集及其语法,请参见上面的“日期格式”。

encoding

可选的 encoding 命令指示提交消息的编码。大多数提交都是 UTF-8,并且编码被省略,但这允许将提交消息导入 git,而无需先对其进行重新编码。

from

from 命令用于指定要从中初始化此分支的提交。此修订将是新提交的第一个祖先。在此提交时构建的树的状态将从 from 提交时的状态开始,并通过此提交中的内容修改进行更改。

在新分支的第一个提交中省略 from 命令将导致 fast-import 创建该提交,该提交没有祖先。这往往只适合项目的初始提交。如果前端在创建新分支时从头开始创建所有文件,则可以使用 merge 命令而不是 from 来使用空树启动提交。在现有分支上省略 from 命令通常是需要的,因为该分支上的当前提交被自动假定为新提交的第一个祖先。

由于 LF 在 Git refname 或 SHA-1 表达式中无效,因此 <commit-ish> 中不支持引号或转义语法。

这里 <commit-ish> 是以下任何内容

  • fast-import 内部分支表中已存在的分支的名称。如果 fast-import 不知道该名称,则将其视为 SHA-1 表达式。

  • 一个标记引用,:<idnum>,其中 <idnum> 是标记号。

    fast-import 使用 : 来表示标记引用的原因是此字符在 Git 分支名称中不合法。前导 : 使得区分标记 42(:42)和分支 42(42refs/heads/42)或碰巧仅由基数 10 数字组成的缩写 SHA-1 变得容易。

    标记必须在使用之前声明(通过 mark)。

  • 完整的 40 字节或缩写的十六进制提交 SHA-1。

  • 任何解析为提交的有效 Git SHA-1 表达式。有关详细信息,请参见 gitrevisions[7] 中的“指定修订”。

  • 特殊的空 SHA-1(40 个零)指定要删除该分支。

从当前分支值重新开始增量导入的特殊情况应写为

	from refs/heads/branch^0

^0 后缀是必需的,因为 fast-import 不允许分支从自身开始,并且该分支是在读取 from 命令之前的内存中创建的。添加 ^0 将强制 fast-import 通过 Git 的修订解析库来解析提交,而不是其内部分支表,从而加载分支的现有值。

merge

包含一个额外的祖先提交。额外的祖先链接不会更改此提交时构建的树状态的方式。如果在创建新分支时省略了 from 命令,则第一个 merge 提交将是当前提交的第一个祖先,并且该分支将以没有文件的状态开始。fast-import 允许每个提交无限数量的 merge 命令,从而建立 n 路合并。

这里 <commit-ish>from 也接受的任何提交规范表达式(见上文)。

filemodify

包含在 commit 命令中,以添加新文件或更改现有文件的内容。此命令有两种不同的方法来指定文件的内容。

外部数据格式

文件的 data 内容已由之前的 blob 命令提供。前端只需要连接它。

	'M' SP <mode> SP <dataref> SP <path> LF

这里通常 <dataref> 必须是 :<idnum>(由之前的 blob 命令设置)或现有 Git blob 对象的完整 40 字节 SHA-1。如果 <mode>040000`,则 <dataref> 必须是现有 Git tree 对象的完整 40 字节 SHA-1 或使用 --import-marks 设置的标记引用。

内联数据格式

文件的 data 内容尚未提供。前端希望将其作为此修改命令的一部分提供。

	'M' SP <mode> SP 'inline' SP <path> LF
	data

有关 data 命令的详细说明,请参见下文。

在这两种格式中,<mode> 是文件条目类型,以八进制表示。Git 仅支持以下模式

  • 100644644:普通(不可执行)文件。大多数项目中的大多数文件都使用此模式。如果有疑问,这就是你想要的。

  • 100755755:普通但可执行的文件。

  • 120000:符号链接,文件的内容将是链接目标。

  • 160000:git 链接,对象的 SHA-1 指向另一个存储库中的提交。Git 链接只能通过 SHA 或通过提交标记指定。它们用于实现子模块。

  • 040000:子目录。子目录只能通过 SHA 或通过使用 --import-marks 设置的树标记指定。

在这两种格式中,<path> 是要添加(如果尚不存在)或修改(如果已存在)的文件的完整路径。

<path> 可以写为不带引号的字节或 C 样式的带引号的字符串。

<path> 不以双引号(")开头时,它是不带引号的字符串,并被解析为没有转义序列的文字字节。但是,如果文件名包含 LF 或以双引号开头,则它不能表示为不带引号的字符串,并且必须用引号括起来。此外,如果 filecopyfilerename 中的源 <path> 包含 SP,则必须用引号括起来。

<path> 以双引号(")开头时,它是 C 样式的带引号的字符串,其中完整的文件名用一对双引号括起来,并使用转义序列。某些字符必须使用反斜杠进行转义:LF 写为 \n,反斜杠写为 \\,双引号写为 \"。某些字符可以选择用转义序列写:\a 表示响铃,\b 表示退格,\f 表示换页,\n 表示换行,\r 表示回车,\t 表示水平制表符,\v 表示垂直制表符。任何字节都可以用 3 位八进制代码写(例如,\033)。所有文件名都可以表示为带引号的字符串。

<path> 必须使用 UNIX 样式的目录分隔符(正斜杠 /),并且其值必须是规范形式。也就是说,它不能

  • 包含空目录组件(例如 foo//bar 无效),

  • 以目录分隔符结尾(例如 foo/ 无效),

  • 以目录分隔符开头(例如 /foo 无效),

  • 包含特殊组件 ...(例如 foo/./barfoo/../bar 无效)。

树的根可以表示为空字符串作为 <path>

<path> 不能包含 NUL,无论是字面意义上的还是以 \000 形式转义的。建议 <path> 始终使用 UTF-8 编码。

filedelete

包含在commit命令中,用于从分支中删除文件或递归删除整个目录。如果文件或目录的删除使其父目录为空,则父目录也将自动删除。这种删除会一直向上级目录传递,直到到达第一个非空目录或根目录。

	'D' SP <path> LF

这里<path> 是要从分支中删除的文件或子目录的完整路径。有关<path>的详细说明,请参见上面的filemodify

filecopy

递归地将现有文件或子目录复制到分支内的另一个位置。现有文件或目录必须存在。如果目标位置存在,它将被从源位置复制的内容完全替换。

	'C' SP <path> SP <path> LF

这里第一个<path>是源位置,第二个<path>是目标位置。有关<path>可能是什么样子的详细说明,请参见上面的filemodify。要使用包含空格的源路径,必须对路径加引号。

filecopy命令立即生效。一旦源位置已复制到目标位置,对源位置应用的任何未来命令都不会影响复制的目标位置。

filerename

将现有文件或子目录重命名到分支内的另一个位置。现有文件或目录必须存在。如果目标位置存在,它将被源目录替换。

	'R' SP <path> SP <path> LF

这里第一个<path>是源位置,第二个<path>是目标位置。有关<path>可能是什么样子的详细说明,请参见上面的filemodify。要使用包含空格的源路径,必须对路径加引号。

filerename命令立即生效。一旦源位置已重命名为目标位置,对源位置应用的任何未来命令将在那里创建新文件,而不会影响重命名的目标位置。

请注意,filerename等同于filecopy后跟filedelete操作。使用filerename会带来轻微的性能优势,但这种优势非常小,因此永远不值得尝试将源代码中的删除/添加对转换为重命名以用于快速导入。提供此filerename命令只是为了简化那些已经拥有重命名信息并且不想费心将其分解为filecopy后跟filedelete操作的前端。

filedeleteall

包含在commit命令中,用于从分支中删除所有文件(以及所有目录)。此命令将内部分支结构重置为没有文件,允许前端随后从头开始添加所有有趣的文件。

	'deleteall' LF

如果前端不知道(或不想知道)当前分支上的哪些文件,因此无法生成用于更新内容的正确filedelete命令,则此命令非常有用。

发出filedeleteall后跟设置正确内容所需的filemodify命令将产生与仅发送所需的filemodifyfiledelete命令相同的结果。但是,filedeleteall方法可能需要快速导入,以便为每个活动分支使用稍微更多的内存(即使对于大多数大型项目,也少于 1 MiB);因此,能够轻松地仅获取提交的受影响路径的前端被鼓励这样做。

notemodify

包含在commit <notes-ref>命令中,用于添加一个新的注释,对<commit-ish>进行注释,或更改此注释内容。在内部,它类似于在<commit-ish>路径上执行100644的filemodify(可能分成子目录)。不建议使用任何其他命令写入<notes-ref>树,除了使用filedeleteall删除此树中所有现有的注释。此命令有两种不同的方法来指定注释的内容。

外部数据格式

注释的数据内容已由先前的blob命令提供。前端只需要将其连接到要注释的提交即可。

	'N' SP <dataref> SP <commit-ish> LF

这里<dataref>可以是先前blob命令设置的标记引用(:<idnum>),也可以是现有 Git blob 对象的完整 40 字节 SHA-1。

内联数据格式

注释的数据内容尚未提供。前端希望在本次修改命令中提供它。

	'N' SP 'inline' SP <commit-ish> LF
	data

有关 data 命令的详细说明,请参见下文。

在两种格式中,<commit-ish>都是from(参见上文)也接受的任何提交规范表达式。

mark

安排快速导入保存当前对象的引用,允许前端在将来某个时间点回忆起此对象,而无需知道其 SHA-1。这里当前对象是mark命令所在的对象创建命令。这可以是committagblob,但commit是最常见的用法。

	'mark' SP ':' <idnum> LF

其中<idnum>是前端为此标记分配的编号。<idnum>的值表示为 ASCII 十进制整数。值 0 是保留的,不能用作标记。只有大于或等于 1 的值可以用作标记。

新标记会自动创建。只需在另一个mark命令中重复使用相同的<idnum>,即可将现有标记移动到另一个对象。

original-oid

提供原始源代码控制系统中对象的名称。快速导入将简单地忽略此指令,但那些在馈送到快速导入之前操作和修改流的过滤器进程可能会对此信息有所用。

	'original-oid' SP <object-identifier> LF

其中<object-identifier>是任何不包含 LF 的字符串。

tag

创建一个指向特定提交的带注释的标签。要创建轻量级(非带注释的)标签,请参见下面的reset命令。

	'tag' SP <name> LF
	mark?
	'from' SP <commit-ish> LF
	original-oid?
	'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
	data

其中<name>是要创建的标签的名称。

标签名称在存储到 Git 时会自动加上前缀refs/tags/,因此导入 CVS 分支符号RELENG-1_0-FINAL只需将RELENG-1_0-FINAL用于<name>,快速导入将相应的引用写入refs/tags/RELENG-1_0-FINAL

<name>的值必须是 Git 中有效的引用名,因此可能包含正斜杠。由于LF在 Git 引用名中无效,因此这里不支持任何引用或转义语法。

from命令与commit命令中的相同;有关详细信息,请参见上文。

tagger命令使用与commit中的committer相同的格式;同样,有关详细信息,请参见上文。

tagger后面的data命令必须提供带注释的标签消息(有关data命令语法,请参见下文)。要导入空标签消息,请使用长度为 0 的数据。标签消息是自由格式的,不会被 Git 解释。目前,它们必须以 UTF-8 编码,因为快速导入不允许指定其他编码。

快速导入不支持在导入过程中签名带注释的标签。尝试包含您自己的 PGP/GPG 签名是不推荐的,因为前端不(容易)访问通常用于此类签名的完整字节集。如果需要签名,请使用reset在快速导入中创建轻量级标签,然后使用标准的git tag过程离线创建这些标签的带注释版本。

reset

创建(或重新创建)命名分支,可选地从特定修订版开始。reset 命令允许前端为现有分支发出新的from命令,或从现有提交创建新分支,而无需创建新提交。

	'reset' SP <ref> LF
	('from' SP <commit-ish> LF)?
	LF?

有关<ref><commit-ish>的详细说明,请参见上面的commitfrom

命令后面的LF是可选的(以前是必需的)。

reset命令还可用于创建轻量级(非带注释的)标签。例如

reset refs/tags/938
from :938

将创建指向标记:938引用的任何提交的轻量级标签refs/tags/938

blob

请求将一个文件修订版写入包文件。修订版未连接到任何提交;此连接必须在随后的commit命令中通过引用 blob 的分配标记来形成。

	'blob' LF
	mark?
	original-oid?
	data

这里的标记命令是可选的,因为某些前端已经选择自行生成 Git SHA-1 以用于 blob,并将该 SHA-1 直接馈送到commit。然而,这通常比值得的要多,因为标记的存储成本很低且易于使用。

data

向快速导入提供原始数据(用于作为 blob/文件内容、提交消息或带注释的标签消息)。可以使用精确的字节数提供数据,也可以用终止行进行分隔。旨在进行生产级转换的真实前端应该始终使用精确字节数格式,因为它更健壮且性能更好。分隔格式主要用于测试快速导入。

出现在data命令的<raw>部分中的注释行始终被视为数据主体的一部分,因此不会被快速导入忽略。这使得导入任何其行可能以#开头的文件/消息内容变得安全。

精确字节数格式

前端必须指定数据的字节数。

	'data' SP <count> LF
	<raw> LF?

其中<count>是出现在<raw>中的确切字节数。<count>的值表示为 ASCII 十进制整数。<raw>两侧的LF不包含在<count>中,也不包含在导入的数据中。

<raw> 之后出现的 LF 是可选的(以前是必需的),但建议使用。始终包含它可以使调试快速导入流更容易,因为下一个命令总是从下一行的第 0 列开始,即使 <raw> 没有以 LF 结尾。

分隔格式

分隔符字符串用于标记数据的结尾。快速导入将通过搜索分隔符来计算长度。此格式主要用于测试,不建议用于真实数据。

	'data' SP '<<' <delim> LF
	<raw> LF
	<delim> LF
	LF?

其中 <delim> 是选定的分隔符字符串。字符串 <delim> 不能单独出现在 <raw> 中的任何一行,否则快速导入将认为数据比实际结束得更早。紧随 <raw> 之后的 LF<raw> 的一部分。这是分隔格式的局限性之一,不可能提供没有 LF 作为其最后一个字节的数据块。

<delim> LF 之后的 LF 是可选的(以前是必需的)。

alias

记录标记引用给定对象,而无需先创建任何新对象。

	'alias' LF
	mark
	'to' SP <commit-ish> LF
	LF?

有关 <commit-ish> 的详细描述,请参见上面 from 下的内容。

checkpoint

强制快速导入关闭当前的 packfile,开始一个新的 packfile,并保存所有当前分支引用、标签和标记。

	'checkpoint' LF
	LF?

请注意,当当前 packfile 达到 --max-pack-size 或 4 GiB(以较小的为准)时,快速导入会自动切换 packfile。在自动 packfile 切换期间,快速导入不会更新分支引用、标签或标记。

由于 checkpoint 可能需要大量的 CPU 时间和磁盘 IO(计算总体的 pack SHA-1 校验和,生成相应的索引文件,并更新引用),因此单个 checkpoint 命令可能需要几分钟才能完成。

前端可以选择在非常大且长时间运行的导入过程中发出检查点,或者当它们需要允许另一个 Git 进程访问分支时。但是,鉴于可以将 30 GiB 的 Subversion 存储库通过快速导入加载到 Git 中,大约需要 3 个小时,因此可能不需要显式检查点。

命令后面的LF是可选的(以前是必需的)。

progress

导致快速导入将完整的 progress 行(未修改)打印到其标准输出通道(文件描述符 1)上,当从输入流中处理该命令时。否则,该命令对当前导入或快速导入的任何内部状态没有任何影响。

	'progress' SP <any> LF
	LF?

命令的 <any> 部分可能包含任何不包含 LF 的字节序列。命令之后的 LF 是可选的。调用者可能希望通过像 sed 这样的工具来处理输出,以删除行的开头部分,例如

frontend | git fast-import | sed 's/^progress //'

checkpoint 之后立即放置 progress 命令将通知读者 checkpoint 已完成,并且可以安全地访问快速导入更新的引用。

get-mark

导致快速导入将对应于标记的 SHA-1 打印到标准输出或之前使用 --cat-blob-fd 参数安排的文件描述符上。否则,该命令对当前导入没有任何影响;它的目的是检索以后的提交可能在其提交消息中引用的 SHA-1。

	'get-mark' SP ':' <idnum> LF

有关如何安全读取此输出的详细信息,请参见下面的“对命令的响应”。

cat-blob

导致快速导入将 blob 打印到之前使用 --cat-blob-fd 参数安排的文件描述符上。否则,该命令对当前导入没有任何影响;它的主要目的是检索可能在快速导入的内存中但无法从目标存储库访问的 blob。

	'cat-blob' SP <dataref> LF

<dataref> 可以是之前设置的标记引用 (:<idnum>) 或 Git blob 的完整 40 字节 SHA-1,预先存在或准备写入。

输出使用与 git cat-file --batch 相同的格式

<sha1> SP 'blob' SP <size> LF
<contents> LF

此命令可以在 filemodify 指令可以出现的地方使用,允许它在提交的中间使用。对于使用内联指令的 filemodify,它也可以出现在 data 指令之前。

有关如何安全读取此输出的详细信息,请参见下面的“对命令的响应”。

ls

将有关路径上的对象的信息打印到之前使用 --cat-blob-fd 参数安排的文件描述符上。这允许从活动提交 (使用 cat-blob) 打印 blob,或从以前的提交复制 blob 或树以在当前提交 (使用 filemodify) 中使用。

ls 命令也可以在 filemodify 指令可以出现的地方使用,允许它在提交的中间使用。

从活动提交读取

此表单只能在 commit 的中间使用。路径命名快速导入的活动提交中的目录条目。在这种情况下,路径必须用引号括起来。

	'ls' SP <path> LF
从命名树读取

<dataref> 可以是标记引用 (:<idnum>) 或 Git 标签、提交或树对象的完整 40 字节 SHA-1,预先存在或等待写入。路径相对于由 <dataref> 命名的树的顶层。

	'ls' SP <dataref> SP <path> LF

有关 <path> 的详细描述,请参见上面的 filemodify

输出使用与 git ls-tree <tree> -- <path> 相同的格式

<mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF

<dataref> 表示 <path> 处的 blob、树或提交对象,可以在以后的 get-markcat-blobfilemodifyls 命令中使用。

如果该路径下没有文件或子树,git fast-import 将改为报告

missing SP <path> LF

有关如何安全读取此输出的详细信息,请参见下面的“对命令的响应”。

feature

要求快速导入支持指定的特性,或者如果它不支持,则中止。

	'feature' SP <feature> ('=' <argument>)? LF

命令的 <feature> 部分可以是以下任何一个

date-format
export-marks
relative-marks
no-relative-marks
force

表现得好像在命令行上传递了相应的命令行选项(以 -- 开头)(见上面的选项)。

import-marks
import-marks-if-exists

与 --import-marks 相似,但有两个方面不同:首先,每个流只允许一个“feature import-marks”或“feature import-marks-if-exists”命令;其次,--import-marks= 或 --import-marks-if-exists 命令行选项会覆盖流中的任何这些“feature”命令;第三,“feature import-marks-if-exists”与相应的命令行选项类似,会静默跳过不存在的文件。

get-mark
cat-blob
ls

要求后端分别支持 get-markcat-blobls 命令。不支持指定命令的快速导入版本将退出并显示一条消息。这使得导入可以在早期以清晰的消息出错,而不是在检测到不支持的命令之前浪费时间进行导入的早期部分。

notes

要求后端支持 commit 命令的 notemodify (N) 子命令。不支持 notes 的快速导入版本将退出并显示一条消息。

done

如果流在没有 done 命令的情况下结束,则出错。如果没有此特性,会导致前端在流中的一个方便点突然结束的错误可能会无法检测到。例如,如果导入前端在中间操作中死亡,而没有在其下属的 git fast-import 实例中发出 SIGTERM 或 SIGKILL,则可能会发生这种情况。

option

处理指定的选项,以便 git fast-import 以适合前端需求的方式运行。请注意,由前端指定的选项将被用户可能指定的任何传递给 git fast-import 本身的选项覆盖。

    'option' SP <option> LF

命令的 <option> 部分可以包含 OPTIONS 部分中列出的任何不更改导入语义的选项,不带前导 --,并且以相同的方式处理。

选项命令必须是输入中的第一个命令(不包括特性命令),在任何非选项命令之后给出选项命令是一个错误。

以下命令行选项会更改导入语义,因此可能不会作为选项传递

  • date-format

  • import-marks

  • export-marks

  • cat-blob-fd

  • force

done

如果 done 特性未在使用,则视为读取 EOF。这可以用来告诉快速导入提前结束。

如果使用 --done 命令行选项或 feature done 命令,则 done 命令是强制性的,并标记流的结束。

对命令的响应

由快速导入写入的新对象不会立即可用。大多数快速导入命令在下一个检查点(或完成)之前没有明显的效果。前端可以发送命令来填充快速导入的输入管道,而无需担心它们需要多长时间才能生效,这通过简化调度来提高性能。

但是,对于某些前端来说,能够在更新时从当前存储库读回数据是有用的(例如,当源材料根据要应用于先前导入的对象的补丁来描述对象时)。这可以通过双向管道连接前端和快速导入来实现

mkfifo fast-import-output
frontend <fast-import-output |
git fast-import >fast-import-output

以这种方式设置的前端可以使用 progressget-marklscat-blob 命令从正在进行的导入中读取信息。

为了避免死锁,此类前端必须在对可能阻塞的快速导入执行写入之前,完全消耗来自 progresslsget-markcat-blob 的任何挂起的输出。

崩溃报告

如果快速导入被提供无效的输入,它将以非零退出状态终止,并在其导入到的 Git 存储库的顶层创建一个崩溃报告。崩溃报告包含快速导入内部状态的快照,以及导致崩溃的最近命令。

崩溃报告中的命令历史记录将显示所有最近的命令(包括流注释、文件更改和进度命令),但崩溃报告中不包括原始文件数据和提交消息。这种排除可以节省报告文件中的空间,并减少快速导入在执行期间必须执行的缓冲量。

在写入崩溃报告后,快速导入将关闭当前的 packfile 并导出标记表。这允许前端开发人员检查存储库状态,并从崩溃的位置恢复导入。修改的分支和标签在崩溃期间不会更新,因为导入没有成功完成。分支和标签信息可以在崩溃报告中找到,如果需要更新,则必须手动应用。

一个崩溃示例

$ cat >in <<END_OF_INPUT
# my very first test commit
commit refs/heads/master
committer Shawn O. Pearce <spearce> 19283 -0400
# who is that guy anyway?
data <<EOF
this is my commit
EOF
M 644 inline .gitignore
data <<EOF
.gitignore
EOF
M 777 inline bob
END_OF_INPUT
$ git fast-import <in
fatal: Corrupt mode: M 777 inline bob
fast-import: dumping crash report to .git/fast_import_crash_8434
$ cat .git/fast_import_crash_8434
fast-import crash report:
    fast-import process: 8434
    parent process     : 1391
    at Sat Sep 1 00:58:12 2007
fatal: Corrupt mode: M 777 inline bob
Most Recent Commands Before Crash
---------------------------------
  # my very first test commit
  commit refs/heads/master
  committer Shawn O. Pearce <spearce> 19283 -0400
  # who is that guy anyway?
  data <<EOF
  M 644 inline .gitignore
  data <<EOF
* M 777 inline bob
Active Branch LRU
-----------------
    active_branches = 1 cur, 5 max
pos  clock name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1)      0 refs/heads/master
Inactive Branches
-----------------
refs/heads/master:
  status      : active loaded dirty
  tip commit  : 0000000000000000000000000000000000000000
  old tree    : 0000000000000000000000000000000000000000
  cur tree    : 0000000000000000000000000000000000000000
  commit clock: 0
  last pack   :
-------------------
END OF CRASH REPORT

技巧和窍门

以下技巧和窍门是从快速导入的各种用户那里收集的,并在这里作为建议提供。

每个提交使用一个标记

在进行存储库转换时,请为每个提交使用一个唯一的标记 (mark :<n>),并在命令行上提供 --export-marks 选项。快速导入将转储一个文件,该文件列出每个标记以及与之对应的 Git 对象 SHA-1。如果前端可以将标记与源存储库联系起来,则可以通过将每个 Git 提交与相应的源修订进行比较,轻松地验证导入的准确性和完整性。

如果您是从 Perforce 或 Subversion 等系统迁移而来,这将非常简单,因为 fast-import 标记也可以是 Perforce 变更集编号或 Subversion 修订版编号。

自由地在分支之间跳转

不要试图优化前端,使其在导入期间始终坚持在一个分支上。虽然这样做可能使 fast-import 速度略微加快,但它往往会大大增加前端代码的复杂性。

fast-import 中内置的分支 LRU 往往表现良好,激活非活动分支的成本非常低,以至于在分支之间跳跃几乎不会影响导入性能。

处理重命名

在导入重命名的文件或目录时,只需在相应的提交期间删除旧名称,并修改新名称。Git 在事后执行重命名检测,而不是在提交期间显式执行。

使用标签修复分支

一些其他 SCM 系统允许用户从多个文件创建标签,这些文件并非来自同一个提交/变更集。或者创建包含存储库中可用文件的子集的标签。

在 Git 中按原样导入这些标签是不可能的,除非至少进行一次提交,该提交“修复”文件以匹配标签的内容。使用 fast-import 的 reset 命令将一个虚拟分支重置到标签的基本提交,该分支位于您的正常分支空间之外,然后提交一个或多个文件修复提交,最后标记虚拟分支。

例如,由于所有正常分支都存储在 refs/heads/ 名称下,因此将标签修复分支命名为 TAG_FIXUP。这样一来,导入程序使用的修复分支就不会与从源导入的真实分支发生命名空间冲突(名称 TAG_FIXUP 不是 refs/heads/TAG_FIXUP)。

在提交修复时,请考虑使用 merge 将提供文件修订版本的提交连接到修复分支。这样做将允许诸如 git blame 之类的工具跟踪真实的提交历史记录并正确地标注源文件。

在 fast-import 终止后,前端需要执行 rm .git/TAG_FIXUP 以删除虚拟分支。

现在导入,稍后重新打包

一旦 fast-import 完成,Git 存储库就完全有效,可以使用了。通常,这只需要很短的时间,即使是相当大的项目(超过 100,000 个提交)。

但是,重新打包存储库是必要的,以提高数据局部性和访问性能。对于非常大的项目(尤其是如果使用了 -f 和较大的 --window 参数),这可能需要几个小时。由于重新打包可以在读取器和写入器并行运行时安全执行,因此在后台运行重新打包,让它完成后再完成。没有理由等到探索新的 Git 项目!

如果您选择等待重新打包,请不要在重新打包完成之前运行基准测试或性能测试。fast-import 输出次优的 pack 文件,这些文件在实际使用情况下根本不会出现。

重新打包历史数据

如果您要重新打包非常旧的导入数据(例如,比去年更旧的数据),请考虑花费一些额外的 CPU 时间并在运行 git repack 时提供 --window=50(或更高)。这将花费更长时间,但也会产生更小的 pack 文件。您只需要花费一次努力,使用您的项目的所有人都将从更小的存储库中受益。

包含一些进度消息

每隔一段时间,让您的前端向 fast-import 发送一条 progress 消息。消息的内容完全是自由格式的,因此一种建议是每当当前提交日期进入下一个月时输出当前月份和年份。您的用户将感到安心,因为他们知道数据流中有多少内容已被处理。

PACKFILE 优化

在打包 blob 时,fast-import 始终尝试针对最后写入的 blob 进行增量化。除非由前端专门安排,否则这可能不是同一文件的先前版本,因此生成的增量将不是最小的。生成的 pack 文件将被压缩,但不会是最优的。

能够有效地访问单个文件的所有修订版本(例如,读取 RCS/CVS ,v 文件)的前端可以选择将该文件的全部修订版本提供为一系列连续的 blob 命令。这允许 fast-import 对不同的文件修订版本进行增量化,从而节省最终 pack 文件的空间。可以使用标记在 commit 命令序列期间识别单个文件修订版本。

fast-import 创建的 pack 文件(s)不鼓励良好的磁盘访问模式。这是因为 fast-import 按在标准输入上接收的顺序写入数据,而 Git 通常在 pack 文件内组织数据,以便最新的(当前提示)数据出现在历史数据之前。Git 还将提交分组在一起,通过更好的缓存局部性加快修订版本遍历速度。

因此,强烈建议用户在 fast-import 完成后使用 git repack -a -d 重新打包存储库,使 Git 能够重新组织 pack 文件,以加快数据访问速度。如果 blob 增量不是最优的(见上文),那么添加 -f 选项强制重新计算所有增量也可以显著减少最终 pack 文件的大小(缩小 30-50% 非常典型)。

除了运行 git repack 外,还可以运行 git gc --aggressive,这将对导入后的一些其他内容进行优化(例如,打包松散的引用)。如 git-gc[1] 中“AGGRESSIVE”部分所述,--aggressive 选项将使用 git-repack[1]-f 选项查找新的增量。由于上面阐述的原因,在 fast-import 之后使用 --aggressive 是少数几个已知值得这样做的情况之一。

内存利用率

有许多因素会影响 fast-import 执行导入所需的内存量。与核心 Git 的关键部分一样,fast-import 使用自己的内存分配器来摊销与 malloc 相关的任何开销。实际上,fast-import 往往会将任何 malloc 开销摊销为 0,因为它使用大块分配。

每个对象

fast-import 为本次执行中写入的每个对象维护一个内存内结构。在 32 位系统上,该结构为 32 字节,在 64 位系统上,该结构为 40 字节(由于指针大小更大)。表中的对象在 fast-import 终止之前不会被释放。在 32 位系统上导入 200 万个对象将需要大约 64 MiB 的内存。

对象表实际上是一个哈希表,它使用对象名称(唯一的 SHA-1)作为键。这种存储配置允许 fast-import 重新使用现有或已写入的对象,并避免将重复项写入输出 pack 文件。在导入中,重复的 blob 非常常见,通常是由于源中的分支合并造成的。

每个标记

标记存储在一个稀疏数组中,每个标记使用 1 个指针(4 字节或 8 字节,取决于指针大小)。虽然数组是稀疏的,但仍然强烈建议前端在 1 到 n 之间使用标记,其中 n 是此导入所需的标记总数。

每个分支

分支被分类为活动分支和非活动分支。两者的内存使用量存在显著差异。

非活动分支存储在一个结构中,该结构使用 96 或 120 字节(分别为 32 位或 64 位系统),加上分支名称的长度(通常小于 200 字节),每个分支。fast-import 可以轻松地在不到 2 MiB 的内存中处理多达 10,000 个非活动分支。

活动分支具有与非活动分支相同的开销,但还包含自分支变为活动以来在该分支上最近修改过的所有树的副本。如果子树 include 自分支变为活动以来未被修改,则不会将它的内容加载到内存中,但如果子树 src 自分支变为活动以来被提交修改过,则会将它的内容加载到内存中。

由于活动分支存储有关该分支上包含的文件的元数据,因此它们的内存内存储大小可能会变得很大(见下文)。

fast-import 基于一个简单的最近最少使用算法自动将活动分支移动到非活动状态。LRU 链在每个 commit 命令上都会更新。可以使用 --active-branches= 命令行选项增加或减少活动分支的最大数量。

每个活动树

树(也称为目录)在其条目所需的内存之上仅使用 12 字节的内存(见下文的“每个活动文件”)。树的成本几乎为 0,因为它的开销在各个文件条目中摊销掉了。

每个活动文件条目

活动树中的文件(以及指向子树的指针)每个条目需要 52 或 64 字节(32/64 位平台)。为了节省空间,文件和树名称在一个公共字符串表中进行合并,这使得文件名“Makefile”无论在项目中出现多少次,只需要 16 字节(包括字符串头开销)。

活动分支 LRU 与文件名字符串池和子树的延迟加载相结合,允许 fast-import 以非常有限的内存占用量(每个活动分支不到 2.7 MiB)有效地导入包含 2,000 多个分支和 45,114 多个文件的项目。

信号

git fast-import 进程发送 SIGUSR1 信号将提前结束当前 pack 文件,模拟 checkpoint 命令。心急的运营商可以使用此功能来查看正在进行的导入中的对象和引用,但这样做会增加运行时间,并且压缩效果更差。

配置

本节中此行以下的所有内容都从 git-config[1] 文档中选择性地包含。内容与那里的一样。

fastimport.unpackLimit

如果 git-fast-import[1] 导入的对象数量低于此限制,则这些对象将被解包到松散的对象文件中。但是,如果导入的对象数量等于或超过此限制,则 pack 将被存储为 pack。从 fast-import 存储 pack 可以使导入操作更快完成,尤其是在速度较慢的文件系统上。如果未设置,则使用 transfer.unpackLimit 的值代替。

另请参见

GIT

git[1] 套件的一部分

scroll-to-top