如何从git归档中排除文件?

18

有一个简单的测试仓库,其中只有一个提交,包含两个文件,ab,我可以获取特定文件的列表:

$ git ls-files a
a

或者列出除特定文件外的所有文件:

$ git ls-files . ':!b'
a

我可以创建特定文件的存档:

$ git archive HEAD a | tar tf -
a

但是我无法创建一个排除特定文件的所有文件的存档:

$ git archive HEAD . ':!b' | tar tf -
a
b

在我的真实代码库中,使用特定文件的存档选项对我来说不可行,因为它超过了最大命令行参数长度。

我知道我可以通过export-ignore属性将要排除的文件列表存储在.gitattributes中,但是这个列表是动态生成的。我可以自动更改该文件,但更改直到另一个提交后才会生效。

是否有其他调用可以在不需要另一个提交的情况下运行?

5个回答

11
我认为你几乎做到了:属性可以从多个位置读取,其中.gitattributes只是其中最常见的一个。第二个位置——被认为是每个仓库的配置文件——是$GIT_DIR/info/attributes
引用手册上的话:
注意,默认情况下,属性是从正在归档的树中的.gitattributes文件中获取的。如果你想在事实上调整输出的方式(例如,你在没有添加适当的export-ignore到它的.gitattributes的情况下进行了提交),请根据需要调整检出的.gitattributes文件,并使用--worktree-attributes选项。或者,您可以将应该应用于归档任何树的必要属性保留在您的$GIT_DIR/info/attributes文件中。
因此,如果可能的话,请将列表粘贴到该文件中,然后执行git archive
另一种方法是不使用git archive,而是仅使用tar工作树,传递tar命令行选项--exclude-from,该选项接受文件。这对于裸仓库无效,但如果您愿意在归档之前检出内容,则可以通过提供正确的$GIT_INDEX_FILE$GIT_WORK_TREE环境变量来执行git read-treegit checkout-index完成。
另一种可能的解决方法是颠倒这种方法:tar(至少GNU tar)支持一个较少使用的选项,即能够通过管道从归档中删除内容。
基本上,你可以这样做:
 $ tar -C a_path -c -f - . \
   | tar -f - --wildcards --delete '*.pdf' >result.tar

这样,管道中的第一个 tar 命令会将所有内容存档,而第二个命令则会将所有文件通过,除非它们与 *.pdf shell glob模式相匹配。

所以,如果可以将使用shell glob模式指定要删除的文件适应于命令行限制,则只需将git archive的输出导入到一个tar进程中,该进程将删除不需要的内容。


感谢您提供详细的答案。我认为对于我来说,.git/info/attributes 不一定是最合适的方法,但它最符合我的需求,如果将来需要更多的功能,我可以将其更改为 tar --delete - user743382
这个答案过于复杂。请参考zett42的回答,使用git归档命令的路径参数和git路径规范的排除功能来排除文件。 - leezu

5
使用Git版本2.20(Windows)和Gitolite服务器(未知版本),我成功地排除了名为“b”的文件和文件夹:
git archive HEAD . ":!b" | tar tf -

这也可以工作:
git archive HEAD . ":(exclude)b" | tar tf -

请注意,在Windows平台上我必须使用双引号,其他平台不确定。
此功能是pathspec的一部分(用于限制Git命令中的路径的模式)。还请参见this answer

谢谢。基于此的实际示例:git archive -v -o eb-bundle.zip --format=zip HEAD . ":(exclude)data/local.js" - Tom Boutell
非常好!有关于“:(exclude)”语法的文档吗?在“git archive”文档中没有提到。 - Lorenzo Donati support Ukraine
@LorenzoDonatisupportUkraine 我已经添加了链接。 - zett42
1
@zett42 谢谢!!!这个信息在GitForWindows的文档中“特别难找”!我本来以为任何需要路径规范的选项/命令都会链接到相关文档,但是没有! - Lorenzo Donati support Ukraine

4
您可以创建一个tar文件,并删除不需要的文件夹和文件。
git archive HEAD -o archive.tar
tar -f archive.tar --delete listoffiles1
tar -f archive.tar --delete listoffiles2
tar -f archive.tar --delete listoffiles..
tar -f archive.tar --delete listoffilesN

这样,您可以拆分命令行,以保持在最大CLI参数长度以下。

3

不要把export-ignore放在已提交的.gitattributes文件中,而是可以把它放在未提交的$GIT_DIR/info/attributes文件中。或者保持.gitattributes未提交并使用--worktree-attributes选项,虽然这样会使你的工作树处于脏的状态,但这也是一种选择。


0

可能存在一个解决方案,即 git archive 需要一个树形结构进行存档。

您正在传递的是 HEAD(可能是最常见的选择)。为了让它做你想要的事情,这个引用会自动解析到它指向的对象上 - 显然会是一个提交。一个提交对象将被解析为与其附加的树对象相连的树对象。所以你得到了当前提交的内容。目前为止都很明显。

但是你可以通过传递任何你想要的树对象来实现!那怎么能帮助呢?嗯,你总是可以使用 git write-tree 从索引的当前状态创建一个树对象 - 它会在标准输出上返回刚刚创建的树对象的 SHA1。你不需要创建提交或其他任何东西。

因此,你可以只需 git rm --cached 掉你不想在 tarball 中出现的所有内容,然后创建一个树对象传递给 git archive。而且由于你不关心该树对象,因此可以将其合并到 git archive 命令中:

git archive $( git write-tree )

然后您可以执行git reset --hard并继续操作。

总之:

git rm --cached foo bar baz
git archive $( git write-tree )
git reset --all

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