Git
简体中文 ▾ Topics ▾ Latest version ▾ git-fast-import last updated in 2.44.0

名称

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

概述

frontend | git fast-import [<多个选项>]

描述

最终用户通常并不想直接运行这个程序。 大多数最终用户都希望使用现有的前端程序,这些程序会解析特定类型的外来源,并将其中存储的内容输入到 git fast-import 中。

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

快速导入后端本身可以导入一个空仓本库(一个已被 git init 初始化的仓库),也可以增量更新一个已填充的仓库。 是否支持从特定外来源增量导入,取决于所使用的前端程序。

选项

--force

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

--quiet

禁用 --stats 显示的输出,使 fast-import 在成功导入时通常保持沉默。 不过,如果导入流中有用于显示用户输出的指令(如 progress 指令),则仍会显示相应的信息。

--stats

显示 fast-import 创建的对象、这些对象存储在哪些文件包中,以及 fast-import 在运行过程中使用的内存等基本统计数据。 默认情况下显示此输出,但可使用 --quiet 关闭此功能。

--allow-unsafe-features

通过使用 featureoption 命令,许多命令行选项可以作为 fast-import 流本身的一部分提供。然而,其中一些选项是不安全的(例如,允许 fast-import 访问仓库之外的文件系统)。这些选项默认是禁用的,但可以通过在命令行中提供该选项来允许。 目前这只影响 export-marksimport-marksimport-marks-if-exists

只有在信任生成快速导入流的程序时,才能启用此选项!对于使用 `import` 功能的远程帮助程序,该选项会自动启用,因为它们已经被信任可以运行自己的代码。

前端的选项

--cat-blob-fd=<fd>

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

--date-format=<格式>

指定前台在 author, committertagger 命令中提供给快速导入的日期类型。 有关支持的格式及其语法的详细信息,请参阅下面的 "日期格式"。

--done

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

标记文件的位置

--export-marks=<文件>

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

--import-marks=<文件>

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

--import-marks-if-exists=<文件>

与 --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=<名称>:<文件>
--rewrite-submodules-to=<名称>:<文件>

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

<名称> 可以是不含冒号的任意字符串,但在指定相应标记时,两个选项必须使用相同的值。 可以使用不同的 <名称> 值指定多个子模块。不在相应的选项对中使用这些选项是错误的。

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

性能和压缩调整

--active-branches=<n>

同时激活的最大分支数。 详情请参阅下文的 "内存利用率"。 默认为 5。

--big-file-threshold=<n>

fast-import 将尝试创建 delta 的 blob 的最大大小,以字节为单位。 默认值为 512m(512MB)。 有些导入者可能希望在内存有限的系统上降低该值。

--depth=<n>

用于 blob 和树形脱模的最大脱模深度。 默认值为 50。

--export-pack-edges=<文件>

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

--max-pack-size=<n>

每个输出包文件的最大大小。 默认为无限大。

fastimport.unpackLimit

参见 git-config[1]

性能

fast-import 的设计使其能够以最少的内存占用和处理时间导入大型项目。 假设前端能够跟上 fast-import 的速度,并向其提供源源不断的数据流,那么对于拥有 10 年以上历史、包含 100,000 多次单独提交的项目,通常只需 1-2 个小时就能完成导入,而且硬件配置也不高(约 2,000 美元)。

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

开发成本

一个典型的快速导入前端往往需要大约 200 行 Perl/Python/Ruby 代码。 大多数开发人员都能在几个小时内创建出可用的导入程序,尽管这是他们第一次接触快速导入,有时甚至是第一次接触 Git。 鉴于大多数转换工具都是一次性工具(用过一次就不再回头),这种情况是最理想的。

并行操作

git pushgit fetch 一样,由 fast-import 处理的导入可以安全地与并行的 git repack -a -d 或 `git 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 不会对其进行解释),快速导入输入格式都是基于文本(ASCII)的。 这种基于文本的格式简化了前端程序的开发和调试,尤其是在使用 Perl、Python 或 Ruby 等高级语言的情况下。

fast-import 对输入的要求非常严格。 我们在下文中所说的 SP 是指一个空格。 同样,LF 表示一个(且只有一个)换行符,HT 表示一个(且只有一个)水平制表符。 提供额外的空格字符会导致意想不到的结果,如分支名称或文件名称中包含前导空格或尾部空格,或当 fast-import 遇到意外输入时提前终止。

流评论

为了帮助调试前端,fast-import 会忽略任何以 #(ASCII 磅/散列)开头的行,包括以 LF 结尾的行。 注释行可以包含任何不包含 LF 的字节序列,因此可以用来包含任何详细的调试信息,这些信息可能是前端特有的,在检查 fast-import 数据流时也很有用。

日期格式

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

raw

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

事件发生的时间由 <时间> 指定,是自 UNIX 纪元(世界协调时 1970 年 1 月 1 日午夜)以来的秒数,并写成 ASCII 十进制整数。

本地偏移量由 <offutc> 指定,与 UTC 的偏移量为正或负。 例如,东部标准时间(比协调世界时晚 5 小时)在 <tz> 中表示为 '0500',而协调世界时为 '+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-epoch 格式,或者可以哄骗源材料以该格式提供日期,或者源材料的格式很容易转换成该格式(因为在解析时不会产生歧义),那么前端程序应优先使用 raw 格式。

now

始终使用当前时间和时区。 必须始终为 <when> 提供字面意义上的 now

这是一种玩具格式。 在通过快速导入创建身份字符串时,系统的当前时间和时区总是被复制到身份字符串中。 无法指定不同的时间或时区。

提供这种格式的原因是,它的实现时间很短,而且可能对想要立即创建新提交的进程很有用,而不需要使用工作目录或 git update-index

如果在 commit 中分别使用了 authoecommitter 命令,时间戳可能会不一致,因为系统时钟会轮询两次(每条命令一次)。 确保作者和提交者身份信息具有相同时间戳的唯一方法是省略 author 命令(因此从 committer 命令复制),或使用除 now 以外的其他日期格式。

命令

fast-import 接受多条命令来更新当前仓库和控制当前导入过程。 稍后将对每条命令进行更详细的讨论(附示例)。

commit

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

tag

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

reset

将现有分支(或新分支)重置为特定版本。 必须使用这条命令,才能在不提交的情况下,将分支更改为特定版本。

blob

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

alias

在不创建任何新对象的情况下,记录一个标记指向一个给定对象。 使用 --import-marks 并引用缺失的标记会导致 fast-import 失败,因此别名可以提供一种方法,将原本已剪枝的提交设置为有效值(例如最近的未剪枝祖先)。

checkpoint

强制 fast-import 关闭当前包文件,生成其唯一的 SHA-1 校验和和索引,并启动一个新的包文件。 此命令为可选命令,执行导入时无需使用。

progress

使 fast-import 将整行内容回传至自己的标准输出。 该命令是可选的,执行导入时不需要它。

done

标记数据流的结束。除非使用 --done 命令行选项或 feature done 命令要求使用 done 功能,否则该命令是可选的。

get-mark

使 fast-import 向使用 --cat-blob-fd 设置的文件描述符打印标记对应的 SHA-1 值,如果未指定,则打印 stdout

cat-blob

使 fast-import 以 cat-file --batch 格式向使用 --cat-blob-fdstdout 设置的文件描述符打印 blob。

ls

使 fast-import 以 ls-tree 格式向使用 --cat-blob-fdstdout 设置的文件描述符打印描述目录条目的行。

feature

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

option

指定 "选项"(OPTIONS)中列出的任何选项,这些选项不会改变流语义以适应前台的需要。此命令为可选项,执行导入时不需要。

commit

用新的提交创建或更新分支,记录项目的逻辑变更。

	'commit' SP <引用> LF
	mark?
	original-oid?
	('author' (SP <名称>)? SP LT <email> GT SP <when> LF)?
	'committer' (SP <名称>)? SP LT <email> GT SP <when> LF
	('encoding' SP <编码>)?
	data
	('from' SP <提交号> LF)?
	('merge' SP <提交号> LF)*
	(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
	LF?

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

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

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

在创建提交之前,可以包含零个或多个 filemodify, filedelete, filecopy, filerename, filedeleteallnotemodify 命令来更新分支的内容。 这些命令可以任意顺序提供。 不过,我们建议在同一提交中的所有 filemodify, filecopy, filerenamenotemodify 命令之前都加上 filedeleteall 命令,因为 filedeleteall 会将分支清除干净(见下文)。

命令后的 LF 是可选的(以前是必须的)。 请注意,出于向后兼容性的考虑,如果提交以一条 data 命令结束(即没有 frommmergefilemodifyfiledeletefilecopyfilerenamefiledeleteallnotemodify 命令),那么命令末尾可能会出现两条 LF 命令,而不是只有一条。

author

如果作者信息可能与提交者信息不同,可选择使用 author 命令。 如果省略了 author 命令,那么 fast-import 将自动使用提交者的信息来处理提交的作者部分。 有关 author 字段的描述,请参阅下文,因为它们与 committer 相同。

committer

committer 命令会显示谁提交了该提交,以及提交的时间。

这里的`<名称>是个人的显示名(例如 ''Com M Itter''),<email>是个人的电子邮件地址(''\cm@example.com'')。 `LTGT 是字面上的小于(\x3c)和大于(\x3e)符号。 需要使用这些符号将电子邮件地址与行中的其他字段分隔开来。 请注意,<名称><email>`是自由格式,可以包含任何字节序列,但 `LTGT 和`LF` 除外。 <名称> 通常采用 UTF-8 编码。

更改的时间由 ‘<when>`指定,并使用 --date-format=<日期格式> 命令行选项选择的日期格式。 有关支持的格式及其语法,请参阅上文的 '日期格式’。

encoding

可选的 encoding 命令表示提交信息的编码。 大多数提交信息都是 UTF-8 编码,编码可以省略,但这样就可以将提交信息导入 git 而无需先重新编码。

from

from 命令用于指定从哪个提交开始初始化分支。 此修订将成为新提交的第一个祖先。 在此提交中构建的树的状态将从 from 提交中的状态开始,并由此提交中的内容修改所改变。

在新分支的第一次提交中省略 from 命令会导致 fast-import 创建的提交没有祖先。这往往只适用于项目的首次提交。 如果前端在创建新分支时从头开始创建所有文件,则可以使用 merge 命令代替 from 命令,以空树开始提交。 在现有分支上,通常希望省略 from 命令,因为该分支上的当前提交会被自动假定为新提交的第一个祖先。

由于 LF 在 Git 引用名称或 SHA-1 表达式中无效,因此 <提交号> 中不支持引号或转义语法。

这里的 <提交号> 是指以下任何一种:

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

  • 标记引用,<idnum>,其中 `<idnum>`是标记编号。

    fast-import 使用 : 表示标记引用的原因是,这个字符在 Git 分支名称中是不合法的。 前导的 : 便于区分标记 42 (:42)和分支 42 (42refs/heads/42),或缩写的 SHA-1,而后者恰好只由基数为 10 的数字组成。

    标记必须先声明(通过 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 路合并。

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

filemodify

包含在 commit 命令中,用于添加新文件或更改现有文件的内容。 该命令有两种指定文件内容的不同方式。

外部数据格式

文件的数据内容已由之前的 blob 命令提供。 前台只需将其连接起来即可。

	'M' SP <模式> SP <引用日期> SP <路径> LF

通常情况下,<数据引用> 必须是由之前的 blob 命令设置的标记引用 (:<idnum>),或者是现有 Git blob 对象的完整 40 字节 SHA-1 值。 如果 <模式>040000`,则 <数据引用> 必须是现有 Git 树对象的完整 40 字节 SHA-1 或用 --import-marks 设置的标记引用。

内联数据格式

文件的数据内容尚未提供。 前台希望将其作为该修改命令的一部分提供。

	'M' SP <模式> SP 'inline' SP <路径> LF
	data

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

在这两种格式中,<模式> 都是以八进制指定的文件类型。 Git 仅支持以下模式:

  • 100644644: 普通(不可执行)文件。 大多数项目中的大部分文件都使用这种模式。 如果不知道用什么格式,请使用这种模式。

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

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

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

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

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

<路径> 字符串必须使用 UNIX 风格的目录分隔符(正斜杠 /),可以包含除 LF 以外的任何字节,且不得以双引号 (") 开头。

路径可以使用 C 风格字符串引号;在任何情况下都可以使用,如果文件名以双引号开头或包含 LF,则必须使用。在 C 风格引号中,完整的名称应该用双引号括起来,任何 LF、反斜杠或双引号字符都必须用反斜杠括起来(例如,"path/with\n, \\ and \" in it")。

<路径> 的值必须是规范格式。也就是说,绝不能是:

  • 包含一个空目录组件(如 foo//bar 就是无效的),

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

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

  • 包含特殊成分 ...(例如,foo/./barfoo/../bar 是无效的)。

树根可以用空字符 <路径> 表示。

建议始终使用 UTF-8 对 <路径> 进行编码。

filedelete

包含在 commit 命令中,用于从分支中删除文件或递归删除整个目录。 如果文件或目录删除后其父级目录为空,父级目录也会自动删除。 这将沿着树向上逐级进行,直到到达第一个非空目录或根目录为止。

	'D' SP <路径> LF

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

filecopy

递归复制现有文件或子目录到分支内的不同位置。 现有文件或目录必须存在。 如果目标位置存在,它将被从源位置复制的内容完全取代。

	'C' SP <路径> SP <路径> LF

这里第一个 <路径> 是源位置,第二个 <路径> 是目标位置。 关于 <路径> 的详细说明,请参阅上文的 filemodify 。 要使用包含 SP 的源路径,路径必须加引号。

filecopy(文件复制)命令会立即生效。 一旦源位置被复制到目标位置,今后应用于源位置的任何命令都不会影响复制的目标位置。

filerename

将现有文件或子目录重命名到分支内的不同位置。 现有文件或目录必须存在。如果目标目录存在,它将被源目录取代。

	'R' SP <路径> SP <路径> LF

这里第一个 <路径> 是源位置,第二个 <路径> 是目标位置。 关于 <路径> 的详细说明,请参阅上文的 filemodify 。 要使用包含 SP 的源路径,路径必须加引号。

filename(文件重命名)命令会立即生效。 一旦源位置被重命名为目标位置,今后应用于源位置的任何命令都将在该位置创建新文件,而不会影响重命名的目标位置。

请注意,filerename 等同于源位置的 filecopyfiledelete。 使用 filerename 在性能上略有优势,但优势非常小,不值得为了快速导入而尝试将源材料中的删除/添加对转换为重命名。 提供这个 fileerename 命令只是为了简化已经有重命名信息的前端程序,而不需要将其分解为 filecopyfiledelete 命令。

filedeleteall

包含在 commit 命令中,用于删除分支中的所有文件(以及所有目录)。 该命令会重置内部分支结构,使其不包含任何文件,从而允许前台随后从头开始添加所有感兴趣的文件。

	'deleteall' LF

如果前台不知道(或不关心知道)当前分支上有哪些文件,因而无法生成适当的 filedelete 命令来更新内容,那么这条命令就非常有用。

在发出 filedeleteall 命令后,再发出所需的 filemodify 命令来设置正确的内容,与只发出所需的 filemodifyfiledelete 命令产生的结果是一样的。 不过,filedeleteall 方法可能会要求 fast-import 在每个活动分支上使用稍多的内存(对于大多数大型项目而言,内存都小于 1MB);因此,我们鼓励能够轻松获取提交中仅受影响路径的前端这样做。

notemodify

包含在`commit` <引用笔记>`命令中,用于为 `<提交号> 添加新注释或更改注释内容。 在内部,它类似于在 <提交号> 路径(可能分成子目录)上的文件修改 100644。除了使用 filedeleteall 命令删除`<notes_ref>` 树中的所有现有注释外,不建议使用任何其他命令向 <引用笔记> 树中写入注释。 该命令有两种指定笔记内容的不同方法。

外部数据格式

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

	'N' SP <引用日期> SP <提交号> LF

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

内联数据格式

备注的数据内容尚未提供。 前台希望将其作为该修改命令的一部分提供。

	'N' SP 'inline' SP <提交号> LF
	data

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

在这两种格式中,<提交号>from 也接受的任何提交规范表达式(见上文)。

mark

安排快速导入保存当前对象的引用,以便前端在未来某个时间点调用该对象,而无需知道其 SHA-1。 在这里,当前对象指的是 mark 命令中出现的对象创建命令。 这可以是 committagblob,但 commit 是最常见的用法。

	'mark' SP ':' <idnum> LF

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

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

original-oid

提供原始源码控制系统中对象的名称。fast-import 将直接忽略此指令,但在向 fast-import 输入数据流之前对其进行操作和修改的过滤进程可能会使用此信息

	'original-oid' SP <对象标识符> LF

其中,<对象标识符> 是任何不包含 LF 的字符串。

tag

创建指向特定提交的注释标记。 要创建轻量级(无注释)标签,请参阅下面的 reset 命令。

	'tag' SP <名称> LF
	mark?
	'from' SP <提交号> LF
	original-oid?
	'tagger' (SP <名称>)? SP LT <email> GT SP <when> LF
	data

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

因此,导入 CVS 分支符号 RELENG-1_0-FINAL 时,只需在 <名称> 中使用 RELENG-1_0-FINAL,而 fast-import 会将相应的引用写成 refs/tags/RELENG-1_0-FINAL

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

from 命令与 commit 命令相同,详见上文。

tagger 命令使用的格式与 commit 中的 committer 相同;详情参见上文。

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

不支持在 fast-import 中导入时对已注释的标记进行签名。 不建议尝试加入自己的 PGP/GPG 签名,因为前端无法(轻易)获取通常用于签名的完整字节集。 如果需要签名,请使用 reset 在 fast-import 中创建轻量级标签,然后使用标准的 git tag 流程离线创建这些标签的注释版本。

reset

创建(或重新创建)指定的分支,可选择从特定版本开始。 重置命令允许前台为现有分支发布新的 from 命令,或从现有提交创建新分支,而无需创建新提交。

	'reset' SP <引用> LF
	('from' SP <提交号> LF)?
	LF?

有关 <引用><提交号> 的详细说明,请参阅上文 commitfrom 部分。

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

reset (重置)命令还可用于创建轻量级(无注释)标记。 例如:

reset refs/tags/938
from :938

将创建轻量级标记 refs/tags/938,并引用提交标记 :938

blob

请求向包文件写入一个文件修订。 修订版与任何提交都没有关联;这种关联必须在随后的 commit 命令中通过指定标记引用 blob 来形成。

	'blob' LF
	mark?
	original-oid?
	data

这里的标记命令是可选的,因为有些前端选择自己为 blob 生成 Git SHA-1,并直接将其输入到 commit 中。 这通常是得不偿失的,因为标记的存储成本很低,而且易于使用。

data

向 fast-import 提供原始数据(用作 blob/文件内容、提交信息或注释标签信息)。 提供的数据可以使用精确的字节数,也可以用终止行分隔。 用于生产质量转换的真正前端应始终使用精确字节数格式,因为这种格式更稳健、性能更好。 分隔格式主要用于测试快速导入。

data 命令的 <raw> 部分中出现的注释行总是被视为数据正文的一部分,因此 fast-import 不会忽略这些注释行。 这样就可以安全导入任何可能以 # 开头的文件/信息内容。

精确字节计数格式

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

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

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

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

分隔格式

fast-import 将通过搜索分隔符来计算长度。 这种格式主要用于测试,不建议用于真实数据。

	'data' SP '<<' <分隔符> LF
	<原始格式> LF
	<分隔符> LF
	LF?

其中 <delim> 是选定的分隔符字符串。 字符串 <delim> 不能单独出现在 <raw> 中的一行,否则 fast-import 会认为数据结束的时间比实际时间早。 紧跟在 <raw> 后面的 LF<raw> 的一部分。 这是分隔格式的限制之一,不可能提供最后一个字节没有 LF 的数据块。

<delim> LF 后面的 LF 是可选的(以前是必须的)。

alias

记录标记指向某个对象,而无需先创建任何新对象。

	'alias' LF
	mark
	'to' SP <提交号> LF
	LF?

有关 <提交号> 的详细说明,请参阅上文 from 部分。

checkpoint

强制 fast-import 关闭当前的打包文件,启动一个新文件,并保存所有当前的分支参考、标记和标记。

	'checkpoint' LF
	LF?

请注意,当当前包文件达到 --max-pack-size 或 4 GiB(以较小者为准)时,fast-import 会自动切换包文件。 在自动切换打包文件期间,fast-import 不会更新分支引用、标记或标记。

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

前端可能会选择在超大、超长的导入过程中,或需要允许另一个 Git 进程访问某个分支时,发出检查点。 不过,通过 fast-import 将一个 30GB 的 Subversion 仓库加载到 Git 大约只需要 3 个小时,因此可能不需要明确的检查点。

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

progress

在处理输入流中的命令时,使 fast-import 将整个 progress 行未经修改地打印到标准输出通道(文件描述符 1)。 除此之外,该命令不会对当前导入或 fast-import 的任何内部状态产生影响。

	'progress' SP <任意> LF
	LF?

命令的 <任意> 部分可包含不含 LF 的任何字节序列。 命令后的 LF 是可选的。 调用者可能希望通过 sed 等工具处理输出,例如删除行的前导部分:

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

checkpoint 之后紧接一个 progress 命令,可以通知阅读器 checkpoint 何时完成,阅读器可以安全地访问快速导入更新的引用。

get-mark

命令 fast-import 将标记对应的 SHA-1 打印到标准输出流或之前用 --cat-blob-fd 参数安排的文件描述符。该命令不会对当前导入产生任何影响;其目的是检索 SHA-1,供以后的提交在提交信息中参考。

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

有关如何安全读取输出结果的详细信息,请参阅下文 '对命令的回复'。

cat-blob

使 fast-import 将 blob 打印到先前用 --cat-blob-fd 参数安排的文件描述符。 该命令不会对当前导入产生任何影响;其主要目的是检索 fast-import 内存中的 blob,但目标存储库无法访问这些 blob。

	'cat-blob' SP <引用日期> LF

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

输出格式与 git cat-file --batch 相同:

<sha1> SP 'blob' SP <尺寸> LF
<内容> LF

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

有关如何安全读取输出结果的详细信息,请参阅下文 '对命令的回复'。

ls

打印文件描述符路径上的对象信息,该文件描述符是之前用 --cat-blob-fd 参数安排的。 这样就可以打印当前提交中的 blob(使用 cat-blob 时),或复制先前提交中的 blob 或树以用于当前提交(使用 filemodify 时)。

ls 命令也可用于出现 filemodify 指令的地方,允许在提交过程中使用。

从活动提交中读取

这种形式只能在 commit 过程中使用。 路径命名 fast-import 活动提交中的目录条目。 在这种情况下,路径必须加引号。

	'ls' SP <路径> LF
从命名树读取数据

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

	'ls' SP <引用日期> SP <路径> LF

有关 <路径> 的详细说明,请参阅上文的 filemodify

输出格式与 `git ls-tree <目录树> — <路径>`相同:

<模式> SP ('blob' | 'tree' | 'commit') SP <引用日期> HT <路径> LF

<数据引用> 表示 <路劲> 中的 blob、树或提交对象,可用于后面的 get-markcat-blobfilemodifyls 命令。

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

missing SP <路径> LF

有关如何安全读取输出结果的详细信息,请参阅下文 '对命令的回复'。

feature

要求 fast-import 支持指定的功能,如果不支持则终止。

	'feature' SP <功能> ('=' <参数>)? LF

命令的 <功能> 部分可以是以下任何一种:

date-format(日期格式)
export-marks(出口标记)
relative-marks
no-relative-marks
强制

就像在命令行中传递了带前导 -- 的相应命令行选项一样(见上文选项)。

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 命令。 不支持指定命令的 fast-import 版本在退出时会给出提示信息。 这样,导入过程中就不会在检测到不支持的命令之前浪费时间,而会在早期就出现明确的错误信息。

备注

要求后端支持 commit 命令的 notemodify(N)子命令。 不支持备注的 fast-import 版本在退出时会显示相关信息。

完成

如果数据流在没有 done (完成)命令的情况下结束,就会出错。 如果没有这项功能,导致前端在流中某个适当位置突然结束的错误就可能不会被发现。 例如,如果一个导入前端在操作中途死亡,而没有在其下属的 git fast-import 实例中发出 SIGTERM 或 SIGKILL,就可能发生这种情况。

option

处理指定的选项,使 git fast-import 的行为符合前端的需要。 注意,前端指定的选项会被用户为 git fast-import 本身指定的任何选项所覆盖。

    'option' SP <选项> LF

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

选项命令必须是输入的第一条命令(不包括功能命令),在任何非选项命令之后给出选项命令都是错误的。

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

  • date-format(日期格式)

  • import-marks(入口标记)

  • export-marks(出口标记)

  • cat-blob-fd

  • 强制

done

如果未使用 done 功能,则视作已读取 EOF。 这可以用来告诉 fast-import 提前结束。

如果使用了 --done 命令行选项或 feature done 命令,则 done 命令是强制性的,标志着数据流的结束。

对命令的响应

快速导入写入的新对象不能立即使用。 大多数 fast-import 命令在下一个检查点(或完成)之前都不会有明显的效果。 前端可以发送命令来填充 fast-import 的输入管道,而不必担心这些命令的生效速度,这样可以简化调度,从而提高性能。

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

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

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

为避免死锁,此类前端在向 fast-import 写入可能阻塞的内容之前,必须完全耗尽来自 progresslsget-markcat-blob 的任何待处理输出。

事故报告

如果 fast-import 收到无效输入,它将以非零的退出状态终止,并在导入的 Git 仓库顶层创建一份崩溃报告。 崩溃报告包含 fast-import 内部状态的快照,以及导致崩溃的最新命令。

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

写完崩溃报告后,fast-import 会关闭当前的包文件并导出标记表。 这样,前端开发人员就可以检查仓库状态,并从崩溃的位置继续导入。 由于导入未成功完成,因此崩溃期间不会更新已修改的分支和标记。 分支和标记信息可在崩溃报告中找到,如果需要更新,必须手动应用。

崩溃实例:

$ cat >in <<END_OF_INPUT
# 我的第一次测试提交
commit refs/heads/master
committer Shawn O. Pearce <spearce> 19283 -0400
# 那家伙到底是谁?
data <<EOF
这是我的提交
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(失败:错误的模式:M 777 )
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

提示和技巧

以下提示和技巧是从 fast-import 的不同用户那里收集的,在此作为建议提供。

每次承诺使用一个标记

在进行仓转换时,每个提交使用一个唯一的标记(标记 :<n>),并在命令行中提供 --export-marks 选项。 如果前端能将标记与源代码仓库绑定,就能通过比较每个 Git 提交和相应的源代码版本,轻松验证导入的准确性和完整性。

从 Perforce 或 Subversion 等系统导入时,这应该非常简单,因为快速导入标记也可以是 Perforce 更改集编号或 Subversion 修订编号。

自由跳转分支

在导入过程中,不要费心去优化前端,使其一次只针对一个分支。 虽然这样做可能会稍微加快快速导入的速度,但往往会大大增加前端代码的复杂性。

fast-import 内置的分支 LRU 通常表现非常出色,而且激活非活动分支的成本很低,在分支间跳来跳去对导入性能几乎没有影响。

处理重命名

在导入重命名的文件或目录时,只需在相应的提交过程中删除旧名称并修改新名称即可。 Git 会在事后执行重命名检测,而不是在提交时明确执行。

使用标签修复分支

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

如果不至少提交一次 '修' 文件以匹配标签内容,就无法在 Git 中原样导入这些标签。 使用 fast-import 的 reset 命令,在正常分支空间外重置一个假分支,使其与标签的基本提交一致,然后提交一次或多次文件修复提交,最后给假分支打上标签。

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

提交修复时,请考虑使用 merge 将提供文件修订的提交连接到修复分支。 这样做可以让 git blame 等工具追踪真实的提交历史,并正确地注释源文件。

快速导入结束后,前台需要执行 rm .git/TAG_FIXUP 来移除虚拟分支。

立即导入,稍后重新打包

快速导入完成后,Git 仓库就完全有效,可以随时使用。 通常这只需要很短的时间,即使对于相当大的项目(100,000+ 次提交)也是如此。

不过,为了提高数据的位置性和访问性能,有必要重新打包仓库。 在超大型项目中,这也可能需要数小时(尤其是在使用 -f 和较大的 --window 参数时)。 由于重新打包可以安全地与读写器同时运行,因此请在后台运行重新打包,并让它在完成时完成。 没有理由等待探索新的 Git 项目!

如果您选择等待重新打包,在重新打包完成之前不要尝试运行基准或性能测试。

重新打包历史数据

如果您要重新打包非常旧的导入数据(例如,比去年更旧的数据),可以考虑在运行 git repack 时花费一些额外的 CPU 时间并提供 --window=50(或更高)的参数。 这样会花费更多时间,但生成的打包文件也会更小。 您只需花费一次精力,而使用您项目的每个人都将受益于更小的仓库。

包括一些进展信息

每隔一段时间,让你的前端向快速导入发出一条 progress(进度)信息。 消息的内容可以完全自由发挥,因此建议在当前提交日期进入下一个月时,输出当前月份和年份。 这样,用户就能更好地了解数据流的处理情况。

包文件优化

在打包 blob 时,fast-import 总是尝试根据最后写入的 blob 进行 deltify。 除非前端特别安排,否则这很可能不是同一文件的先前版本,因此生成的 delta 不会是尽可能小的。 生成的打包文件将被压缩,但不会达到最佳效果。

可以高效访问单个文件所有修订版本的前端(例如读取 RCS/CVS ,v 文件),可以选择以连续的 blob 命令序列提供该文件的所有修订版本。 这样,快速导入程序就能将不同的文件修订版本相互区分开来,从而节省最终打包文件的空间。 标记可用于以后在一连串 commit 命令中识别单个文件修订版。

fast-import 创建的包文件不支持良好的磁盘访问模式。 这是因为 fast-import 是按照标准输入的顺序写入数据的,而 Git 通常会在包文件中组织数据,使最新的(当前提示)数据出现在历史数据之前。 Git 还会将提交聚类在一起,通过更好的缓存定位加快版本遍历速度。

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

你也可以不运行 git repack 而运行 git gc--aggressive,它也会在导入后优化其他事情(比如打包松散的引用)。正如 git-gc[1] 中的 "AGRESSIVE "部分所述,--aggressive 选项会在 git-repack[1] 中使用 -f 选项找到新的 deltas。由于上述原因,在快速导入后使用 --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 重用现有或已写入的对象,避免将重复的对象写入输出包文件。 在导入过程中,重复的 blob 会非常常见,这通常是由于源代码中的分支合并造成的。

每个标记

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

每个分支

分支分为活动分支和非活动分支。 这两类分支的内存使用量差别很大。

非活动分支存储在一个结构中,每个分支使用 96 或 120 字节(分别用于 32 位或 64 位系统),外加分支名称的长度(通常小于 200 字节)。

活动分支的开销与非活动分支相同,但也包含该分支上最近修改过的每棵树的副本。 如果子树 include 在分支变为活动后没有被修改过,其内容就不会被加载到内存中,但如果子树 src 在分支变为活动后被提交修改过,其内容就会被加载到内存中。

由于活动分支存储的是该分支所含文件的元数据,因此其内存存储容量可能会相当大(见下文)。

fast-import 根据一种简单的最近最少使用算法,自动将活动分支移至非活动状态。 LRU 链会在每一条 commit 命令中更新。 可以在命令行中使用 --active-branches= 来增加或减少活动分支的最大数量。

每个活动目录树

树(又称目录)在其条目(见下文 '每个活动文件')所需内存之外,仅占用 12 字节内存。 树的成本几乎为零,因为其开销会在单个文件条目中摊销。

每个活动文件条目

活动目录树中的文件(和指向子目录树的指针)每个条目需要 52 或 64 字节(32/64 位平台)。 为了节省空间,文件名和目录树名被集中到一个通用的字符串表中,这样无论文件名 'Makefile' 在项目中出现多少次,都只需使用 16 个字节(包括字符串头开销)。

活动分支 LRU 与文件名字符串池和子树的懒加载相结合,使 fast-import 能够在非常有限的内存空间内(每个活动分支小于 2.7 MiB)高效导入包含 2,000 多个分支和 45,114 多个文件的项目。

信号

git fast-import 进程发送 SIGUSR1 会提前结束当前的打包文件,模拟一个 checkpoint 命令。 没有耐心的操作员可以使用这一工具来偷看正在导入的对象和引用,但代价是增加运行时间和降低压缩率。

配置

Warning

Missing zh_HANS-CN/includes/cmd-config-section-all.txt

See original version for this content.

Warning

Missing zh_HANS-CN/config/fastimport.txt

See original version for this content.

GIT

属于 git[1] 文档

scroll-to-top