如何使用Git进行子模块稀疏校验



有很多关于稀疏签出的文章和SO问题。不幸的是,我没有找到具体的例子。我想得到以下示例工作:

创建子模块

cd ~
mkdir sub && cd $_
git init 
mkdir foo && touch $_/foo
mkdir bar && touch $_/bar
git add .
git commit -am "Initial commit"

创建项目

cd ~
mkdir project && cd $_
git init
git submodule add ../sub sub
git config -f .gitmodules submodule.sub.shallow true
git config -f .gitmodules submodule.sub.sparsecheckout true
echo foo/* > .git/modules/sub/info/sparse-checkout
git commit -am "Initial commit"
git submodule update
cd sub
git checkout .

在这一点上,我希望sub文件夹只包含foo/foo而不包含bar。不幸的是,它不起作用:

$ ls
bar/ foo/

我怎样才能让它工作?

git submodule add本身检查子模块。

对我来说成功的是:

git init
# I did not find a way to add submodule in 1 step without checking out
git clone --depth=1 --no-checkout ../sub sub
git submodule add ../sub sub
git submodule absorbgitdirs
# note there is no "submodule.sub.sparsecheckout" key
git -C sub config core.sparseCheckout true
# note quoted wildcards to avoid their expansion by shell
echo 'foo/*' >>.git/modules/sub/info/sparse-checkout
git submodule update --force --checkout sub

或者从git URL:

# Initialize main repo (if needed)
git init
# Checkout the to-be submodule
# I did not find a way to add submodule in 1 step without checking out
git clone --depth=1 --no-checkout git@example.com:username/some-repo.git path/some-repo
# Add as a submodule
git submodule add git@example.com:username/some-repo.git path/some-repo
# Move the .git dir from path/some-repo/.git into parent repo's .git 
git submodule absorbgitdirs
# Note there is no "submodule.sub.sparsecheckout" key
git -C sub config core.sparseCheckout true
# This pattern determines which files within some-repo.git get checked out. 
# Note quoted wildcards to avoid their expansion by shell
echo 'foo/*'  >> .git/modules/path/some-repo/info/sparse-checkout
# Actually do the checkout
git submodule update --force --checkout sub

添加到max630的答案:

  • 自Git 2.25(2020年第一季度)以来,您将使用新命令git sparse-checkout

  • Git 2.28(2020年第三季度)记录了sparse checkout设置对子模块的影响。

这意味着,如果您使主存储库本身稀疏地签出,除了子模块(已经稀疏,如max630的答案所示)之外,在主存储库上使用git sparse-checkout不会对子模块产生负面影响(即,在其中有工作进行时错误地删除它)。

参见Elijah Newren(newren)提交的e7d7c73(2020年6月10日)
(由Junio C Hamano合并——gitster——于2020年6月22日提交81be89e)

git-sparse-checkout:澄清与子模块的交互

签字:Elijah Newren
审核:Derrick Stolee

暂时忽略稀疏签出功能,如果一个人有一个子模块,并在其中使用未推送的更改创建本地分支,并可能向其中添加一些未跟踪的文件,那么我们希望避免意外删除这样的子模块。

因此,例如使用git.git,如果您运行

git checkout v2.13.0

则sha1collisiondetection/子模块不会被删除,即使它在v2.14.0之前并没有作为子模块存在。

类似地,如果您之前只签出v2.13.0并运行

git checkout v2.14.0

sha1冲突检测/子模块不会自动初始化,尽管它是v2.14.0的一部分。

在这两种情况下,git都要求分别初始化或去初始化子模块。

此外,我们还对其他命令中的子模块进行了特殊处理,如clean,它需要两个--force标志来删除未跟踪的子模块,并且一些命令具有--recurse-submodules标志。

稀疏签出与签出非常相似,类似的名称证明了这一点——它从工作副本中添加和删除文件。

然而,出于同样的避免数据丢失的原因,我们不想在签出时从工作副本中删除子模块,也不想在稀疏签出时删除子模块。

因此,子模块需要单独初始化或去初始化;更改稀疏签出规则不应自动触发子模块的删除或生动化。

我相信git sparse-checkout之前关于子模块的措辞只是关于这个特定的问题。

不幸的是,前面的措辞可能被解释为意味着,无论稀疏性模式如何,子模块都应被视为活跃的。

更新措辞以避免产生这种暗示。

考虑措辞差异变得重要的两种示例情况可能会有所帮助:

在未来,我们希望用户能够运行这样的命令

git clone --sparse=moduleA --recurse-submodules $REPO_URL

并且具有自动设置的稀疏性路径,并且具有在稀疏性路径内的子模块被自动初始化。

我们不希望任何路径中的所有子模块都使用该命令自动初始化。

同样,我们希望能够做这样的事情

git -c sparse.restrictCmds grep --recurse-submodules $REV $PATTERN

并且在CCD_ 14中搜索所记录的稀疏性模式内的CCD_。

我们希望它递归到那些稀疏模式中的子模块中,但不希望在搜索可能的子模块时递归到与稀疏模式不匹配的目录中。

所以文档现在包括:

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

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

此外,上述事实意味着存在多种原因;被跟踪的";工作副本中可能不存在文件:稀疏签出的稀疏模式应用程序,以及子模块初始化状态
因此,像git grep这样处理工作副本中被跟踪文件的命令可能会返回受其中一个或两个限制的结果。


使用Git 2.31(2021年第一季度),";CCD_ 24">(man)已被调整为仅限于稀疏结账路径。

由于稀疏签出子模块中可能需要git grep,因此这一点非常重要。

参见Matheus Tavares提交的42d906b(2021年2月9日)(matheustavares)
(由Junio C Hamano合并——gitster——提交628c13c,2021年2月25日)

grep:在工作树搜索上执行稀疏签出

建议人:Elijah Newren

在稀疏签出存储库中,当条目与搜索路径规范匹配并设置了SKIP_WORKTREE位时,git grep(man)(不带--cached)最终会搜索缓存。

这既令人困惑,因为稀疏路径不应出现在工作树搜索中(因为它们没有被检出),也因为输出混合了工作树和缓存结果而没有区分它们
(请注意,grep也会在包括--assume-unchanged路径的工作树搜索中使用缓存。
但在这种情况下,关键是假设索引项和文件的内容相同。
这不适用于稀疏路径的情况,在稀疏路径中,文件甚至不应存在。)

通过教导grep遵守工作树搜索的稀疏签出规则来解决这个问题
如果用户希望在当前稀疏签出定义之外grep路径,他们可以更新稀疏性规则以实现文件,也可以使用--cached搜索索引中注册的所有Blob。

而且,仍然适用于稀疏的检出子模块:

Git 2.39(2022年第四季度);CCD_ 35">(man)学会了在稀疏结账中更懒惰地按需扩展稀疏索引。

参见袁绍轩提交的7cae762(2022年9月22日)(ffyuanda)
(由Junio C Hamano合并——gitster——于2022年10月10日提交67bf4a8)

builtin/grep.c:与稀疏索引集成

建议人:Derrick Stolee
协助人:Derek Stolee
协助者:Victoria Dye
协助者;Elijah Newren
签字人:袁绍轩

启用稀疏索引并删除ensure_full_index()

在此补丁之前,git-grep使用ensure_full_index()方法来扩展索引并搜索所有条目
由于此方法需要遍历所有树并构建索引,因此它是整个命令中速度较慢的部分。

为了获得更好的性能,该补丁使用grep_tree()来搜索稀疏目录条目,并摆脱了ensure_full_index()方法。

为什么grep_tree()ensure_full_index()更好?

  1. grep_tree()ensure_full_index()一样正确
    grep_tree()在索引上循环时递归地查找每个稀疏目录条目(由树表示),这样做的结果与扩展索引的结果相匹配。

  2. grep_tree()利用路径规范来限制搜索范围
    ensure_full_index()始终扩展索引,这意味着它将始终遍历repo中的所有树和Blob,而不关心用户是否只想要内容的子集,即使用路径规范
    另一方面,grep_tree()将只搜索与路径规范匹配的内容,因此可能会少走几棵树。

  3. grep_tree()不构造并复制回新索引,而ensure_full_index()构造并复制。这也节省了一些时间。

最新更新