简体中文 ▾ 主题 ▾ 最新版本 ▾ git-sparse-checkout 最后更新于 2.52.0

名称

git-sparse-checkout - 将工作树缩减至已跟踪文件的子集

概要

git sparse-checkout (init | list | set | add | reapply | disable | check-rules | clean) [<options>]

描述

此命令用于创建稀疏检出,将工作树从包含所有已跟踪文件更改为仅包含这些文件的子集。它还可以切换当前存在的子集文件,或撤销操作并返回到在工作副本中包含所有已跟踪文件的状态。

文件子集的选择可以通过在锥形(cone)模式(默认)下提供目录列表,或在非锥形模式下提供模式(patterns)列表来完成。

处于稀疏检出状态时,其他 Git 命令的行为会略有不同。例如,切换分支不会更新稀疏检出目录/模式之外的路径,并且 git commit -a 不会将稀疏检出目录/模式之外的路径记录为已删除。

此命令目前处于实验阶段。其行为以及其他命令在存在稀疏检出时的行为,在未来可能会发生变化。

命令

list

列出稀疏检出(sparse-checkout)文件中的目录或模式。

set

如果必要的稀疏检出配置设置(core.sparseCheckoutcore.sparseCheckoutConeindex.sparse)尚未设置为所需值,则启用它们;根据 set 子命令后的参数列表填充稀疏检出文件,并更新工作目录以匹配。

为了确保在某个工作树中调整稀疏检出设置不会改变其他工作树的设置,set 子命令会将您的仓库配置升级为使用特定于工作树的配置(如果尚未启用)。由 set 子命令参数定义的稀疏性存储在工作树特定的稀疏检出文件中。有关更多详细信息,请参阅 git-worktree[1]git-config[1]extensions.worktreeConfig 的文档。

当提供 --stdin 选项时,将从标准输入读取以换行符分隔的目录或模式列表,而不是从参数中读取。

默认情况下,输入列表被视为目录列表,与 git ls-tree -d --name-only 的输出相匹配。这包括将以双引号 (") 开头的路径名解释为 C 风格的引用字符串。请注意,指定目录下的所有文件(任何深度)都将包含在稀疏检出中,指定目录或其任何父目录的同级文件也将被包含(详见下文的 锥形模式集)。在过去,这并非默认行为,需要指定 --cone 或启用 core.sparseCheckoutCone

当传递 --no-cone 时,输入列表被视为模式列表。此模式有许多缺点,包括无法与 --sparse-index 等某些选项配合使用。如下文“非锥形模式的问题”部分所述,我们不建议使用它。

使用 --[no-]sparse-index 选项来决定是否使用稀疏索引(默认不使用)。稀疏索引通过缩小索引规模,使其与您的稀疏检出定义更紧密地对齐。这对于 git statusgit add 等命令具有显著的性能优势。此功能仍处于实验阶段。在某些命令与该功能完全集成之前,使用稀疏索引可能会变慢。

警告: 使用稀疏索引需要以一种外部工具无法完全理解的方式修改索引。如果您遇到兼容性问题,请运行 git sparse-checkout init --no-sparse-index 将索引重写为非稀疏格式。旧版本的 Git 无法理解稀疏目录条目索引扩展,在禁用该功能之前,可能无法与您的仓库交互。

add

更新稀疏检出文件以包含额外的目录(在锥形模式下)或模式(在非锥形模式下)。默认情况下,这些目录或模式从命令行参数读取,但也可以使用 --stdin 选项从标准输入读取。

reapply

重新将稀疏模式规则应用于工作树中的路径。诸如 merge 或 rebase 之类的命令可能会为了执行工作(例如为了向您展示冲突)而使路径实体化,而其他稀疏检出命令可能会因为某些原因(例如文件有未暂存的更改或冲突)而无法对单个文件进行稀疏处理。在这种情况下,在清理受影响的路径(例如解决冲突、撤销或提交更改等)后运行 git sparse-checkout reapply 是有意义的。

reapply 命令还可以接受 --[no-]cone--[no-]sparse-index 标志,其含义与 set 命令中的标志相同,以便在不需要重新指定所有稀疏路径的情况下更改您正在使用的稀疏模式。

clean

择机删除稀疏检出定义之外的文件。此命令要求在锥形模式下使用递归目录匹配来确定应删除哪些文件。如果一个文件包含在稀疏检出定义之外的已跟踪目录中,则会被考虑删除。

某些特殊情况(例如合并冲突或稀疏检出定义之外的修改文件)可能会导致保留本应删除的文件。解决冲突、暂存修改,并配合使用 git sparse-checkout reapplygit sparse-checkout clean 来解决这些情况。

此命令可用于确保稀疏索引高效工作,尽管它不要求通过 index.sparse=true 配置启用稀疏索引功能。

为防止意外删除工作树文件,除非将 clean.requireForce 配置选项设置为 false,否则 clean 子命令在没有 -f--force 选项的情况下不会删除任何文件。

--dry-run 选项将列出将被删除的目录,而不实际删除它们。以这种模式运行有助于预测 clean 命令的行为,或确定稀疏目录中留下了哪些类型的文件。

--verbose 选项将列出考虑删除的目录中的每一个文件。此选项有助于确定这些文件是否真的很重要,或者解释为什么尽管当前处于稀疏检出状态,该目录仍然存在。

disable

禁用 core.sparseCheckout 配置设置,并恢复工作目录以包含所有文件。

init

已弃用的命令,其行为类似于不带指定路径的 set。未来可能会被删除。

在历史上,set 并不处理所有必要的配置设置,这意味着必须先后调用 initset。同时调用这两者意味着 init 步骤会首先删除几乎所有已跟踪文件(在锥形模式下还会删除被忽略的文件),然后 set 步骤会重新添加许多已跟踪文件(但不包括被忽略的文件)。除了文件丢失外,这种组合的性能和用户界面体验都很差。

此外,在历史上,如果稀疏检出文件已经存在,init 实际上不会初始化它。这意味着可以返回到稀疏检出,而无需记住要在随后的 setadd 命令中传递哪些路径。但是,由于 --cone--sparse-index 选项在 disable 命令后不会被记住,因此调用普通的 init 来轻松恢复的实用性降低了。

check-rules

检查稀疏性规则是否匹配一个或多个路径。

默认情况下,check-rules 从标准输入读取路径列表,并仅输出匹配当前稀疏性规则的路径。输入预期为每行一个路径,与 git ls-tree --name-only 的输出一致,包括以双引号 (") 开头的路径名被解释为 C 风格的引用字符串。

当带 --rules-file <file> 标志调用时,输入文件将与 <file> 中找到的稀疏检出规则(而非当前规则)进行匹配。文件中的规则预期与 git sparse-checkout set --stdin 接受的格式相同(特别是必须以换行符分隔)。

默认情况下,传递给 --rules-file 选项的规则被解释为锥形模式目录。要通过 --rules-file 传递非锥形模式模式,请将该选项与 --no-cone 选项结合使用。

当带 -z 标志调用时,标准输入上的路径格式以及输出路径都以 \0 终止且不加引号。请注意,这不适用于通过 --rules-file 选项传递的规则格式。

示例

git sparse-checkout set MY/DIR1 SUB/DIR2

切换到稀疏检出,工作副本中存在 MY/DIR1/ 和 SUB/DIR2/ 下的所有文件(任何深度),以及 MY/ 和 SUB/ 直接下方的所有文件和顶层目录下的所有文件。如果已处于稀疏检出状态,则将工作副本中存在的文件更改为此新选择。请注意,此命令还将删除任何不再包含已跟踪文件或非忽略未跟踪文件的目录中的所有被忽略文件。

git sparse-checkout disable

重新向工作目录填充所有文件,禁用稀疏检出。

git sparse-checkout add SOME/DIR/ECTORY

将 SOME/DIR/ECTORY/ 下的所有文件(任何深度)以及 SOME/DIR/ 和 SOME/ 直接下方的所有文件添加到稀疏检出中。在使用此命令之前,必须已处于稀疏检出状态。

git sparse-checkout reapply

某些命令可能会以不遵循所选稀疏目录的方式更新工作树。这可能源于 Git 外部工具写入文件,甚至是由于特殊情况(如合并/变基时遇到冲突)或某些命令不完全支持稀疏检出(例如旧的 recursive 合并后端仅提供有限支持)而影响 Git 命令。此命令重新应用现有的稀疏目录规范,使工作目录匹配。

内部原理 — 稀疏检出

“稀疏检出”允许稀疏地填充工作目录。它使用 skip-worktree 位(参见 git-update-index[1])来告知 Git 工作目录中的某个文件是否值得关注。如果设置了 skip-worktree 位,且该文件在工作树中不存在,则忽略其缺失。Git 会避免填充这些文件的内容,这使得稀疏检出在处理包含大量文件但只有少数文件对当前用户重要的仓库时非常有用。

$GIT_DIR/info/sparse-checkout 文件用于定义 skip-worktree 参考位图。当 Git 更新工作目录时,它会根据此文件更新索引中的 skip-worktree 位。匹配文件中模式的文件将出现在工作目录中,其余文件则不会。

内部原理 — 非锥形模式的问题

setadd 子命令填充的 $GIT_DIR/info/sparse-checkout 文件被定义为一组使用与 .gitignore 文件相同语法的模式(每行一个)。在锥形模式下,这些模式仅限于匹配目录(用户只需提供或查看目录名称),而在非锥形模式下,允许使用任何 gitignore 风格的模式。在非锥形模式下使用完整的 gitignore 风格模式有许多缺点:

  • 从根本上说,它使得各种工作树更新过程(pull、merge、rebase、switch、reset、checkout 等)需要进行 O(N*M) 次模式匹配,其中 N 是模式数量,M 是索引中的路径数量。这种扩展性很差。

  • 必须通过指定前导目录名或通配符来限制模式数量,从而避免扩展性问题。

  • 在命令行上传递通配符(globs)容易出错,因为用户可能会忘记给通配符加引号,导致 shell 将其扩展为所有匹配的文件,并将其逐个传递给 sparse-checkout set/add。虽然对于 "git grep *.c" 等命令也可能存在类似问题,但 grep/log/status 的错误会立即体现在输出中。而在 sparse-checkout 中,错误会在运行命令时被记录,可能直到用户稍后切换分支、变基或合并时才会显现,从而在用户出错和发现错误之间造成了延迟。

  • 与前一项相关,sparse-checkout 有 add 子命令但没有 remove 子命令。即使添加了 remove 子命令,撤销由于未加引号的通配符导致的意外添加也有“删除过多”的风险,因为它可能会删除意外添加之前就已经包含的条目。

  • 非锥形模式使用 gitignore 风格的模式来选择要包含的内容(取反模式除外),而 .gitignore 文件使用 gitignore 风格模式来选择要排除的内容(取反模式除外)。关于 gitignore 风格模式的文档通常不以匹配或不匹配来描述,而是以用户想要“排除”的内容来描述。这可能会给试图学习如何指定稀疏检出模式以获得所需行为的用户带来困惑。

  • 每一个想要提供某种“特殊路径模式匹配”的其他 git 子命令都使用 pathspecs,但稀疏检出的非锥形模式却使用 gitignore 模式,这感觉不一致。

  • 它存在一些“正确”行为不明确的边缘情况。两个例子:

    首先,两个用户都在一个子目录中,第一个用户运行:

    git sparse-checkout set '/toplevel-dir/*.c'

    而第二个用户运行:

    git sparse-checkout set relative-dir

    在插入稀疏检出文件之前,这些参数是否应该被转译为:

    current/subdirectory/toplevel-dir/*.c

    current/subdirectory/relative-dir

    ?输入第一条命令的用户可能意识到 set/add 的参数在非锥形模式下应该是模式,可能不会喜欢这种转译。然而,许多 gitignore 风格的模式仅仅是路径,这可能正是输入第二条命令的用户所想的,如果他们的参数没被转译,他们会很不高兴。

    其次,对于非锥形模式用户的 set/add 命令,bash-completion 应该补全什么?如果它建议路径,是否加剧了上述问题?此外,如果它建议路径,万一用户的文件或目录名以 !# 开头,或者包含 *\?[] 怎么办?如果它建议路径,它会将 "/pro" 补全为根文件系统中的 "/proc",还是当前目录下的 "/progress.txt"?(注意,在非锥形模式下,用户很可能希望路径以领先的 / 开头,原因与 .gitignore 文件通常包含斜杠相同。)在所有这些情况下,对文件或目录进行补全可能会带来糟糕的意外。

  • 过度的灵活性使得其他扩展基本上变得不切实际。--sparse-index 在非锥形模式下可能无法实现;即使某种程度上可行,实现的工程量也会大得多,而且在实践中可能太慢。一些关于将部分克隆与稀疏检出耦合的想法也只有在更受限的路径集下才具有实际意义。

由于所有这些原因,非锥形模式已被弃用。请切换到使用锥形模式。

内部原理 — 锥形模式处理

“锥形模式”(默认模式)允许您仅指定要包含的目录。对于指定的任何目录,该目录下的所有路径都将被包含,并且前导目录(包括顶层目录)直接下方的任何路径也将被包含。因此,如果您指定了目录 Documentation/technical/,那么您的稀疏检出将包含:

  • 顶层目录中的所有文件

  • Documentation/ 直接下方的所有文件

  • Documentation/technical/ 下任何深度的所有文件

此外,在锥形模式下,即使没有指定目录,顶层目录中的文件也会被包含。

在锥形模式下更改稀疏检出模式时,Git 会检查不在稀疏检出锥体(cone)内的每个已跟踪目录,查看其是否包含任何未跟踪文件。如果所有这些文件都因 .gitignore 模式而被忽略,则该目录将被删除。如果该目录内有任何未跟踪文件未被忽略,则该目录内不会发生删除操作,并会显示警告消息。如果这些文件很重要,请重置稀疏检出定义以便包含它们,使用 git addgit commit 存储它们,然后手动删除任何剩余文件,以确保 Git 能以最佳状态运行。

另请参阅“内部原理 — 锥形模式集”部分,了解目录在底层是如何转化为稀疏检出全模式集的子集的。

内部原理 — 全模式集

全模式集允许任意模式匹配和复杂的包含/排除规则。更新索引时,这些规则可能导致 O(N*M) 次模式匹配,其中 N 是模式数量,M 是索引中的路径数量。为了解决此性能问题,当启用 core.sparseCheckoutCone 时,允许使用更受限的模式集。

稀疏检出文件使用与 .gitignore 文件相同的语法;详见 gitignore[5]。不过,在这里,模式通常用于选择要包含哪些文件,而不是排除哪些文件。(不过,这可能会有点令人困惑,因为 gitignore 风格的模式具有由以 ! 开头的模式定义的取反功能,因此您也可以选择包含的文件。)

例如,要选择所有内容,然后删除 unwanted 文件(以便工作树中出现除名为 unwanted 的文件之外的所有文件):

git sparse-checkout set --no-cone '/*' '!unwanted'

这些模式会原样放入 $GIT_DIR/info/sparse-checkout,因此此时该文件的内容将是:

/*
!unwanted

另请参阅 git-read-tree[1] 的“稀疏检出”部分,了解更多关于稀疏检出中使用的 gitignore 风格模式的信息。

内部原理 — 锥形模式集

在锥形模式下,仅接受目录,但它们会被转换为全模式集中使用的相同 gitignore 风格模式。我们将这些模式分为以下两种类型之一:

  1. 递归(Recursive): 包含目录内的所有路径。

  2. 父级(Parent): 包含目录直接下方的所有文件。

由于锥形模式总是包含顶层文件,当运行不带指定目录的 git sparse-checkout set 时,顶层目录会被添加为父级模式。此时,稀疏检出文件包含以下模式:

/*
!/*/

这表示“包含顶层目录直接下方的所有内容,但不包含其下任何层级的内容”。

在锥形模式下,git sparse-checkout set 子命令接受目录列表。命令 git sparse-checkout set A/B/C 将目录 A/B/C 设置为递归模式,目录 AA/B 被添加为父级模式。生成的稀疏检出文件现在为:

/*
!/*/
/A/
!/A/*/
/A/B/
!/A/B/*/
/A/B/C/

在这里,顺序很重要,因此负模式会被文件中位置较低的正模式覆盖。

除非明确将 core.sparseCheckoutCone 设置为 false,否则 Git 将解析稀疏检出文件并预期这些类型的模式。如果模式不匹配,Git 将发出警告。如果模式确实匹配预期格式,Git 将使用更快的基于哈希的算法来计算稀疏检出的包含情况。如果不匹配,无论如何设置,git 的行为都将如同 core.sparseCheckoutCone 为 false 一样。

在锥形模式下,尽管完整的模式被写入了 $GIT_DIR/info/sparse-checkout 文件,但 git sparse-checkout list 子命令将列出定义递归模式的目录。对于上述示例稀疏检出文件,输出如下:

$ git sparse-checkout list
A/B/C

如果 core.ignoreCase=true,模式匹配算法将使用不区分大小写的检查。这会纠正 git sparse-checkout set 命令中大小写不匹配的文件名,以反映工作目录中预期的锥形区域。

内部原理 — 子模块

如果您的仓库包含一个或多个子模块,则子模块的填充基于与 git submodule 命令的交互。具体来说,git submodule init -- <path> 将确保 <path> 处的子模块存在,而 git submodule deinit [-f] -- <path> 将删除 <path> 处子模块的文件(包括任何未跟踪文件、未提交更改和未推送的历史记录)。类似于稀疏检出如何从工作树中删除文件但在索引中保留条目,反初始化的子模块会从工作目录中删除,但在索引中仍有一个条目。

由于子模块可能有未推送的更改或未跟踪的文件,删除它们可能会导致数据丢失。因此,更改稀疏包含/排除规则不会导致已检出的子模块从工作副本中删除。换句话说,正如即使在删除或添加子模块的分支之间切换时,checkout 也不会导致子模块自动删除或初始化一样,使用 sparse-checkout 来缩小或扩大“感兴趣”文件的范围也不会导致子模块自动反初始化或初始化。

此外,上述事实意味着“已跟踪”文件可能不在工作副本中存在的原因有多种:稀疏检出的稀疏模式应用,以及子模块的初始化状态。因此,像 git grep 这样在工作副本中的已跟踪文件上运行的命令,其返回结果可能会受到其中一种或两种限制的影响。

GIT

Git[1] 套件的一部分