GIT:查找包括子模块在内的文件列表(例如使用git ls-files)

9

我一直在尝试找到获取包括子模块中所有文件的git存储库列表的方法。目前,git ls-files只会提供顶级子模块目录,但不包括子模块中的文件。进一步调查后,我发现可以使用git submodule递归查找所有子模块,然后使用git ls-files,具体命令如下:

git submodule --quiet foreach --recursive "git ls-files"

这样做的唯一问题是结果是子模块中的路径,但我需要从存储库获取完整路径。因此,对于以下内容:

例如:/some/path/to/gitrepo/source/submodule/[file1, file2]

我看到的是:

file1
file2

我希望看到的是:

source/submodule/file1
source/submodule/file2

有没有一种方法可以实现这个?从文档中看,有一些预定义的变量($name、$path、$sha1和$toplevel),但我不确定如何使用它们来获得所需的结果。

1
注意:你需要使用 Git 2.11+ (Q4 2016) 中的 git ls-files --recurse-submodules 命令。请参考我的答案。它可以在主仓库中运行,并输出完整路径。 - VonC
2个回答

12

另一种方法可以使用Git 2.11+(Q4 2016)

git ls-files --recurse-submodules

查看 提交 75a6315, 提交 07c01b9, 提交 e77aa33, 提交 74866d7 (2016年10月7日) 由 Brandon Williams (mbrandonw) 提交。
(由 Junio C Hamano -- gitster -- 合并于 提交 1c2b1f7, 2016年10月26日)

ls-files: 可选择递归到子模块

"git ls-files" 学会了 "--recurse-submodules" 选项,可以用于获取跨子模块的已跟踪文件列表(即仅适用于 "--cached" 选项,而不适用于列出未跟踪或被忽略的文件)。

这将是一个有用的工具,位于管道的上游,使用 xargs 读取所有顶级超级项目的工作树文件。

在此测试中 所示,输出将包括从主父存储库开始的文件完整路径。

git ls-files文档

现在包括:
--recurse-submodules

递归调用ls-files命令,对存储库中的每个子模块进行操作。
目前仅支持--cached模式。
Git 2.13 (Q2 2017)增加了对ls-files --recurse-submodules的健壮性:
请参见提交记录2cfe66a提交记录2e5d650(由Jacob Keller (jacob-keller)于2017年4月13日提交)。
(由Junio C Hamano -- gitster --于2017年4月24日合并至提交记录2d646e3

ls-files:修复嵌套子模块的recurse-submodules

自从提交e77aa33(“ls-files:可选地递归到子模块”,2016年10月7日,git 2.11)以来,ls-files已知道如何在显示文件时递归到子模块。
不幸的是,某些情况下会失败,包括嵌套多个子模块、从一个本身具有子模块的子模块中调用或设置了GIT_DIR环境变量时。
在提交b58a68c(“setup:允许将前缀传递给git命令”,2017年3月17日,git 2.13-rc0)之前,这会导致错误,指示--prefix--super-prefix不兼容。
在此提交之后,相反,进程将永远循环,并将GIT_DIR设置为父级,并持续读取父子模块文件并无限递归。
通过为子模块正确准备环境来解决此问题,类似于其他命令(例如grep)的行为。
作为Git 2.29(2020年第四季度)的注释,配置项submodule.recurse无法使用。
请参见提交7d15fdb(2020年10月4日),作者是Philippe Blain(phil-blain
(由Junio C Hamano -- gitster --提交9d19e17中合并,2020年10月5日)

gitsubmodules doc:使用“--recurse-submodules”调用'ls-files'

由Philippe Blain签署

git ls-files(man)从未被教导尊重submodule.recurse配置变量,现在改变已为时过晚,但该命令仍在'gitsubmodules(7)'中被提及,好像它确实尊重该配置。
通过使用'--recurse-submodules'选项调用'ls-files'来调整'gitsubmodules(7)'中的调用。

gitsubmodules现在在其手册页面中包括以下内容:

git ls-files --recurse-submodules

[注意]
git ls-files也需要自己的--recurse-submodules标志。


Git 2.36 (2022年第二季度), 还支持git ls-files --stage --recurse-submodule命令。


在 Git 2.40(2023年第一季度)中,停止使用 git --super-prefix 并将其使用范围缩小到子模块助手。

请查看 提交记录 4002ec3, 提交记录 f5a6be9, 提交记录 04f1fab, 提交记录 99a32d8, 提交记录 677c981, 提交记录 bb61a96, 提交记录 f0a5e5a, 提交记录 49eb1d3 (2022年12月20日) 作者为 Ævar Arnfjörð Bjarmason (avar)
请查看 提交记录 0d1806e (2022年12月20日) 作者为 Glen Choo (chooglen)
(由Junio C Hamano -- gitster --合并于提交记录 d4c5400, 2023年1月5日)

read-tree: 增加“--super-prefix”选项,消除全局变量

签名作者:Ævar Arnfjörð Bjarmason

"--super-prefix"选项最初是在提交 74866d7 ("git: make super-prefix option", 2016-10-07, Git v2.11.0-rc0 -- merge listed in batch #11)中添加的,用于:
  • 与"ls-files"一起使用(提交 e77aa33 ("ls-files: optionally recurse into submodules", 2016-10-07, Git v2.11.0-rc0 -- merge listed in batch #11)),不久之后
  • "submodule--helper"(提交 89c8626 ("submodule helper: support super prefix", 2016-12-08, Git v2.12.0-rc0 -- merge listed in batch #5))和
  • "grep"(提交 0281e48 ("grep: optionally recurse into submodules", 2016-12-16, Git v2.12.0-rc0 -- merge listed in batch #6))。
直到提交 3d41542 ("unpack-trees: support super-prefix option", 2017-01-17, Git v2.12.0-rc0 -- merge),"read-tree"才开始使用它。 当时有道理,但自那时以来,我们已经在提交 188dce1 ("ls-files: use repository object", 2017-06-22, Git v2.14.0-rc0 -- merge listed in batch #14)中使"ls-files"递归处理,在提交 f9ee2fc ("grep: recurse in-process using 'struct repository'", 2017-08-02, Git v2.15.0-rc0 -- merge listed in batch #2)中使"grep"递归处理,最后是在前几个提交中使"submodule--helper"递归处理。 让我们还从"read-tree"中删除它,这样就可以将选项从"git"本身中删除。 我们可以这样做,因为它的仅存用户是子模块API,该API现在将使用其新的"--super-prefix"选项调用"read-tree"。 只有在调用"submodule_move_head()"函数时才会这样做。 然后,"submodule_move_head()"函数只被"read-tree"本身调用,但现在,我们不再在cmd_read_tree()之间设置环境变量来传递"--super-prefix",而是: -在"struct unpack_trees_options""中设置一个新的"super_prefix"

git现在在其手册页面中包含了以下内容:

[--config-env==] []


4
请查看git submodule文档,其中写着:

foreach

在每个已检出子模块中执行任意shell命令。 该命令可以访问变量$name, $path, $sha1$toplevel:$name是.gitmodules文件中对应子模块部分的名称, $path是相对于超级项目的子模块目录名称, $sha1是作为超级项目记录的提交,而$toplevel是超级项目顶层的绝对路径。

结合上述信息,您可以执行以下操作:
git submodule foreach 'git ls-files | sed "s|^|$path/|"'

在这个例子中,我们只是从子模块中获取git ls-files的输出,并使用sed在前面添加$path的值,即子模块相对于父项目顶级目录的路径。

1
如果$path包含'|'(或任何其他用于分隔sed的字符),则技术上是错误的。您可以通过以下方式使其更加健壮:git submodule --quiet foreach 'export path;bash -c '\''git ls-files | sed "s/^/${path/\//\\/}\//"'\' - cdleonard
尽管sed技巧有问题,但我喜欢这个答案。它指向了'git submodule foreach'。我执行了[ git submodule foreach 'git ls-files --others --exclude-standard' ]来列出准备添加的文件。 - grenix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接