Git 中文参考(六)(5)https://developer.aliyun.com/article/1565806
性能
快速导入的设计允许它以最小的内存使用量和处理时间导入大型项目。假设前端能够跟上快速导入并为其提供恒定的数据流,那么拥有 10 年以上历史且包含 100,000 多个单独提交的项目的导入时间通常只需 1-2 小时即可完成( 〜$ 2,000 USD)硬件。
大多数瓶颈似乎是在外部源数据访问(源无法足够快地提取修订版)或磁盘 IO(快速导入写入速度与磁盘将获取数据一样快)。如果源数据存储在与目标 Git 存储库不同的驱动器上(由于较少的 IO 争用),导入将运行得更快。
发展成本
快速导入的典型前端往往占据大约 200 行 Perl / Python / Ruby 代码。大多数开发人员能够在短短几个小时内创建工作进口商,即使这是他们第一次快速导入,有时甚至是 Git。这是一个理想的情况,因为大多数转换工具都是丢弃的(使用一次,永不回头)。
并行操作
像 git push 或 git fetch 一样,快速导入处理的导入可以安全地与并行git repack -a -d
或git gc
调用或任何其他 Git 操作(包括 )一起运行 git prune ,因为松散的对象永远不会被快速导入使用)。
快速导入不会锁定分支或标记引用它正在主动导入。导入之后,在其 ref 更新阶段,快速导入测试每个现有分支 ref 以验证更新将是快进更新(存储在 ref 中的提交包含在要写入的提交的新历史中)。如果更新不是快进更新,则快速导入将跳过更新该 ref,而是打印警告消息。快速导入将始终尝试更新所有分支引用,并且不会在第一次失败时停止。
可以使用–force 强制分支更新,但建议仅在其他安静的存储库上使用它。初始导入空存储库不需要使用–force。
技术讨论
快速导入跟踪内存中的一组分支。通过在输入流上发送commit
命令,可以在导入过程中的任何时刻创建或修改任何分支。此设计允许前端程序同时处理无限数量的分支,按源数据中可用的顺序生成提交。它还大大简化了前端程序。
fast-import 不使用或更改当前工作目录或其中的任何文件。 (但它会更新当前的 Git 存储库,由GIT_DIR
引用。)因此,导入前端可以将工作目录用于其自身目的,例如从外部源提取文件修订。这种对工作目录的无知也允许快速导入非常快速地运行,因为在分支之间切换时不需要执行任何昂贵的文件更新操作。
输入格式
除了原始文件数据(Git 不解释)之外,快速导入输入格式是基于文本(ASCII)的。这种基于文本的格式简化了前端程序的开发和调试,特别是在使用更高级别的语言(如 Perl,Python 或 Ruby)时。
快速导入对其输入非常严格。我们在下面说 SP,我们的意思是正好是一个空格。同样 LF 意味着一个(并且只有一个)换行和 HT 一个(并且只有一个)水平标签。提供额外的空白字符将导致意外结果,例如名称中带有前导或尾随空格的分支名称或文件名,或者遇到意外输入时提前终止快速导入。
流评论
为了帮助调试前端,快速导入忽略以#
(ASCII 磅/散列)开头直到并包括以LF
结束的行的任何行。注释行可以包含不包含 LF 的任何字节序列,因此可以用于包括可能特定于前端的任何详细调试信息,并且在检查快速导入数据流时非常有用。
日期格式
支持以下日期格式。前端应通过在–date-format =< fmt>中传递格式名称来选择将用于此导入的格式。命令行选项。
raw
这是 Git 原生格式,是 SP
。如果未指定–date-format,它也是快速导入的默认格式。
事件的时间由指定为自 UNIX 纪元(午夜,1970 年 1 月 1 日,UTC)以来的秒数,并写为 ASCII 十进制整数。
本地偏移由指定为 UTC 的正偏移或负偏移。例如,EST(比 UTC 晚 5 小时)将在
中用“-0500”表示,而 UTC 是“+0000”。局部偏移不会影响
;它仅用作帮助格式化例程显示时间戳的建议。
如果源材料中没有本地偏移,请使用“+0000”或最常见的本地偏移。例如,许多组织都有一个 CVS 存储库,该存储库只能由位于相同位置和时区的用户访问。在这种情况下,可以假设与 UTC 的合理偏移。
与rfc2822
格式不同,此格式非常严格。格式的任何变化都会导致快速导入以拒绝该值。
rfc2822
这是 RFC 2822 描述的标准电子邮件格式。
示例值是“Tue Feb 6 11:22:18 2007 -0500”。 Git 解析器是准确的,但在宽松的一面。它是 git am 在应用从电子邮件收到的补丁时使用的解析器。
某些格式错误的字符串可能被接受为有效日期。在某些情况下,Git 仍然可以从格式错误的字符串中获取正确的日期。还有一些类型的格式错误的字符串,Git 会解析错误,但认为有效。严重错误的字符串将被拒绝。
与上面的raw
格式不同,RFC 2822 日期字符串中包含的时区/ UTC 偏移信息用于在存储之前将日期值调整为 UTC。因此,重要的是这些信息应尽可能准确。
如果源材料使用 RFC 2822 样式日期,前端应该让快速导入处理解析和转换(而不是尝试自己做),因为 Git 解析器已在野外进行了很好的测试。
如果源材料已经使用 UNIX-epoch 格式,前端应该更喜欢raw
格式,可以通过哄骗来提供该格式的日期,或者其格式可以轻松转换为格式,因为解析时没有歧义。
now
始终使用当前时间和时区。必须始终为提供文字
now
。
这是一种玩具形式。此系统的当前时间和时区始终在通过快速导入创建时复制到标识字符串中。无法指定不同的时间或时区。
提供此特定格式是因为它很难实现,并且可能对想要立即创建新提交的进程有用,而无需使用工作目录或 git update-index 。
如果在commit
中使用单独的author
和committer
命令,则时间戳可能不匹配,因为系统时钟将被轮询两次(每个命令一次)。确保作者和提交者身份信息具有相同时间戳的唯一方法是省略author
(从而从committer
复制)或使用now
以外的日期格式。
命令
fast-import 接受几个命令来更新当前存储库并控制当前导入过程。稍后将对每个命令进行更详细的讨论(带有示例)。
commit
通过创建新提交并更新分支以指向新创建的提交来创建新分支或更新现有分支。
tag
从现有提交或分支创建带注释的标记对象。此命令不支持轻量级标记,因为不建议用于记录有意义的时间点。
reset
将现有分支(或新分支)重置为特定修订。必须使用此命令将分支更改为特定修订,而不对其进行提交。
blob
将原始文件数据转换为 blob,以备将来在commit
命令中使用。此命令是可选的,不需要执行导入。
checkpoint
强制快速导入以关闭当前 packfile,生成其唯一的 SHA-1 校验和和索引,并启动新的 packfile。此命令是可选的,不需要执行导入。
progress
导致快速导入以将整行回显到其自己的标准输出。此命令是可选的,不需要执行导入。
done
标记流的结尾。除非使用--done
命令行选项或feature done
命令请求done
功能,否则此命令是可选的。
get-mark
导致快速导入将对应于标记的 SHA-1 打印到使用--cat-blob-fd
设置的文件描述符,或者如果未指定则打印stdout
。
cat-blob
导致快速导入将 cat-file --batch 格式的 blob 打印到使用--cat-blob-fd
或stdout
设置的文件描述符(如果未指定)。
ls
导致快速导入将以 ls-tree 格式描述目录条目的行打印到使用--cat-blob-fd
或stdout
设置的文件描述符(如果未指定)。
feature
启用指定的功能。这要求快速导入支持指定的功能,如果不支持则中止。
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 data ('from' SP <commit-ish> LF)? ('merge' SP <commit-ish> LF)? (filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)* LF?
其中是要进行提交的分支的名称。通常,分支名称在 Git 中以
refs/heads/
为前缀,因此导入 CVS 分支符号RELENG-1_0
将使用refs/heads/RELENG-1_0
作为的值。
的值必须是 Git 中的有效 refname。由于
LF
在 Git refname 中无效,因此此处不支持引用或转义语法。
可以选择出现mark
命令,请求快速导入以保存对新创建的提交的引用,以供将来使用(参见下面的格式)。前端标记它们创建的每个提交是很常见的,从而允许从任何导入的提交创建未来的分支。
committer
后面的data
命令必须提供提交消息(参见下面的data
命令语法)。要导入空提交消息,请使用 0 长度数据。提交消息是自由格式的,不由 Git 解释。目前它们必须以 UTF-8 编码,因为快速导入不允许指定其他编码。
可以包括零个或多个filemodify
,filedelete
,filecopy
,filerename
,filedeleteall
和notemodify
命令以在创建提交之前更新分支的内容。可以按任何顺序提供这些命令。但是,建议filemodify
命令在同一次提交中的所有filemodify
,filecopy
,filerename
和notemodify
命令之前,因为filedeleteall
擦除分支清除(见下文)。
命令后的LF
是可选的(以前是必需的)。
author
如果作者信息可能与提交者信息不同,则可以选择显示author
命令。如果省略author
,则 fast-import 将自动使用提交者作者部分的提交者信息。有关author
中字段的说明,请参见下文,因为它们与committer
相同。
committer
committer
命令指示谁进行了此提交,以及何时进行此提交。
这里是此人的显示名称(例如“Com M Itter”),
是该人的电子邮件地址(“cm@example.com”)。
LT
和GT
是文字小于(\ x3c)和大于(\ x3e)的符号。这些是从行中的其他字段分隔电子邮件地址所必需的。注意和
是自由形式的,可以包含任何字节序列,
LT
,GT
和LF
除外。 通常是 UTF-8 编码的。
更改时间由使用–date-format =< fmt>选择的日期格式指定。命令行选项。有关支持的格式集及其语法,请参阅上面的“日期格式”。
from
from
命令用于指定从中初始化此分支的提交。此修订将是新提交的第一个祖先。在此提交时构建的树的状态将从from
提交的状态开始,并由此提交中的内容修改进行更改。
在第一次提交新分支时省略from
命令将导致快速导入以创建没有祖先的提交。这通常仅适用于项目的初始提交。如果前端在创建新分支时从头开始创建所有文件,则可以使用merge
命令代替from
以使用空树启动提交。通常需要在现有分支上省略from
命令,因为该分支上的当前提交被自动假定为新提交的第一个祖先。
由于LF
在 Git refname 或 SHA-1 表达式中无效,因此中不支持引用或转义语法。
这里是以下任何一种:
- 已存在于 fast-import 的内部分支表中的现有分支的名称。如果快速导入不知道名称,则将其视为 SHA-1 表达式。
- 标记参考
:
,其中是标记号。
快速导入使用:
表示标记引用的原因是此字符在 Git 分支名称中不合法。前导:
可以很容易地区分标记 42(:42
)和分支 42(42
或refs/heads/42
),或简化的 SHA-1,它恰好只包含 10 位数字。
必须先声明标记(通过mark
)才能使用它们。 - 完整的 40 字节或缩写提交 SHA-1(十六进制)。
- 任何解析为提交的有效 Git SHA-1 表达式。有关详细信息,请参阅 gitrevisions [7] 中的“指定修订”。
- 特殊的空 SHA-1(40 个零)指定要删除分支。
从当前分支值重新启动增量导入的特殊情况应写为:
from refs/heads/branch⁰
⁰
后缀是必需的,因为快速导入不允许分支从自身开始,并且在从输入中读取from
命令之前在内存中创建分支。添加⁰
将强制快速导入通过 Git 的修订解析库而不是其内部分支表来解析提交,从而加载分支的现有值。
merge
包括一个额外的祖先提交。附加的祖先链接不会更改此提交时构建树状态的方式。如果在创建新分支时省略from
命令,则第一个merge
提交将是当前提交的第一个祖先,并且分支将从没有文件开始。快速导入允许每次提交无限数量的merge
命令,从而建立 n 路合并。
这里是
from
也接受的任何提交规范表达式(见上文)。
filemodify
包含在commit
命令中以添加新文件或更改现有文件的内容。此命令有两种不同的方法来指定文件的内容。
External data format
该文件的数据内容已由先前的blob
命令提供。前端只需要连接它。
'M' SP <mode> SP <dataref> SP <path> LF
这里通常必须是先前
blob
命令设置的标记引用(:
),或者是现有 Git blob 对象的完整 40 字节 SHA-1。如果为
040000``,则
必须是现有 Git 树对象的完整 40 字节 SHA-1 或使用
–import-marks`设置的标记引用。
Inline data format
尚未提供该文件的数据内容。前端想要将其作为此修改命令的一部分提供。
'M' SP <mode> SP 'inline' SP <path> LF data
有关data
命令的详细说明,请参见下文。
在两种格式中,是以八进制指定的文件条目类型。 Git 仅支持以下模式:
100644
或644
:普通(不可执行)文件。大多数项目中的大多数文件都使用此模式。如果有疑问,这就是你想要的。100755
或755
:正常但可执行的文件。120000
:符号链接,文件内容将是链接目标。160000
:gitlink,对象的 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/./bar
和foo/../bar
无效)。
树的根可以用空字符串表示为。
建议始终使用 UTF-8 对进行编码。
filedelete
包含在commit
命令中以删除文件或从分支中递归删除整个目录。如果删除文件或目录使其父目录为空,则父目录也将自动删除。这会将树级联,直到到达第一个非空目录或根目录。
'D' SP <path> LF
这里是要从分支中删除的文件或子目录的完整路径。有关
的详细说明,请参见上面的
filemodify
。
filecopy
递归地将现有文件或子目录复制到分支内的其他位置。现有文件或目录必须存在。如果目标存在,它将完全被从源复制的内容替换。
'C' SP <path> SP <path> LF
这里第一个是源位置,第二个
是目的地。有关
的详细描述,请参见上面的
filemodify
。要使用包含 SP 的源路径,必须引用该路径。
filecopy
命令立即生效。将源位置复制到目标后,应用于源位置的任何将来的命令都不会影响副本的目标。
filerename
将现有文件或子目录重命名为分支内的其他位置。现有文件或目录必须存在。如果目标存在,它将被源目录替换。
'R' SP <path> SP <path> LF
这里第一个是源位置,第二个
是目的地。有关
的详细描述,请参见上面的
filemodify
。要使用包含 SP 的源路径,必须引用该路径。
filerename
命令立即生效。将源位置重命名为目标后,应用于源位置的任何将来命令都将在其中创建新文件,而不会影响重命名的目标。
请注意,filerename
与filecopy
后跟源位置的filedelete
相同。使用filerename
有一个轻微的性能优势,但优势是如此之小,以至于永远不值得尝试将源材料中的删除/添加对转换为重命名以进行快速导入。提供此filerename
命令只是为了简化已经具有重命名信息的前端,并且不希望将其分解为filecopy
后跟filedelete
。
filedeleteall
包含在commit
命令中,以从分支中删除所有文件(以及所有目录)。此命令重置内部分支结构,使其中没有文件,允许前端随后从头开始添加所有有趣的文件。
'deleteall' LF
如果前端不知道(或不关心)当前在分支上的文件,并且因此无法生成正确的filedelete
命令来更新内容,则此命令非常有用。
发出filedeleteall
后跟所需的filemodify
命令来设置正确的内容将产生与仅发送所需的filemodify
和filedelete
命令相同的结果。然而,filedeleteall
方法可能需要快速导入,以便为每个活动分支使用稍多的内存(即使是大多数大型项目也不到 1 MiB);鼓励这样做的前端只能轻松获取提交的受影响路径。
notemodify
包含在commit
命令中,用于添加注释
的新注释或更改此注释内容。在内部,它类似于
路径上的 filemodify 100644(可能拆分为子目录)。除了
filedeleteall
之外,不建议使用任何其他命令写入树以删除此树中的所有现有注释。此命令有两种不同的方法来指定注释的内容。
External data format
该注释的数据内容已由先前的blob
命令提供。前端只需要将它连接到要注释的提交。
'N' SP <dataref> SP <commit-ish> LF
这里可以是先前
blob
命令设置的标记引用(:
),也可以是现有 Git blob 对象的完整 40 字节 SHA-1。
Inline data format
该笔记的数据内容尚未提供。前端想要将其作为此修改命令的一部分提供。
'N' SP 'inline' SP <commit-ish> LF data
有关data
命令的详细说明,请参见下文。
在两种格式中,是
from
也接受的任何提交规范表达式(见上文)。
mark
安排快速导入以保存对当前对象的引用,允许前端在未来的某个时间点调用此对象,而不知道它的 SHA-1。这里当前对象是mark
命令出现的对象创建命令。这可以是commit
,tag
和blob
,但commit
是最常用的用法。
'mark' SP ':' <idnum> LF
其中是前端指定给该标记的数字。
的值表示为 ASCII 十进制整数。值 0 保留,不能用作标记。只有大于或等于 1 的值才可用作标记。
自动创建新标记。只需在另一个mark
命令中重复使用相同的,就可以将现有标记移动到另一个对象。
original-oid
提供原始源代码管理系统中对象的名称。快速导入将简单地忽略该指令,但是在进行快速导入之前对进行操作和修改流的过滤进程可能会使用此信息
'original-oid' SP <object-identifier> LF
其中是任何不包含 LF 的字符串。
tag
创建引用特定提交的带注释标记。要创建轻量级(非注释)标记,请参阅下面的reset
命令。
'tag' SP <name> LF 'from' SP <commit-ish> LF original-oid? 'tagger' (SP <name>)? SP LT <email> GT SP <when> LF data
其中是要创建的标记的名称。
当存储在 Git 中时,标签名称会自动以refs/tags/
为前缀,因此导入 CVS 分支符号RELENG-1_0-FINAL
将仅使用RELENG-1_0-FINAL
作为,快速导入会将相应的 ref 写为
refs/tags/RELENG-1_0-FINAL
。
的值必须是 Git 中的有效 refname,因此可能包含正斜杠。由于
LF
在 Git refname 中无效,因此此处不支持引用或转义语法。
from
命令与commit
命令相同;见上文了解详情。
tagger
命令使用与commit
中committer
相同的格式;再看上面的细节。
tagger
后面的data
命令必须提供带注释的标签消息(参见下面的data
命令语法)。要导入空标记消息,请使用 0 长度数据。标记消息是自由格式的,不由 Git 解释。目前它们必须以 UTF-8 编码,因为快速导入不允许指定其他编码。
不支持在快速导入中导入期间对带注释的标签进行签名。建议不要尝试包含您自己的 PGP / GPG 签名,因为前端不能(轻松)访问通常进入此类签名的完整字节集。如果需要签名,请使用reset
从快速导入中创建轻量级标记,然后使用标准 _git 标记 _ 进程离线创建这些标记的带注释版本。
reset
创建(或重新创建)命名分支,可选择从特定修订开始。 reset 命令允许前端为现有分支发出新的from
命令,或者从现有提交创建新分支而不创建新提交。
'reset' SP <ref> LF ('from' SP <commit-ish> LF)? LF?
有关和
的详细说明,请参见上文
commit
和from
。
命令后的LF
是可选的(以前是必需的)。
reset
命令也可用于创建轻量级(非注释)标记。例如:
reset refs/tags/938 from :938
将创建轻量级标签refs/tags/938
引用任何提交标记:938
引用。
blob
请求将一个文件修订版写入 packfile。修订与任何提交无关;必须通过指定标记引用 blob,在后续commit
命令中形成此连接。
'blob' LF mark? original-oid? data
mark 命令在这里是可选的,因为一些前端选择为它们自己生成 blob 的 Git SHA-1,并直接将其提供给commit
。然而,这通常比它的价值更多,因为标记的存储成本低且易于使用。
Git 中文参考(六)(7)https://developer.aliyun.com/article/1565808