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

How do I exclude files from git archive?

给定一个简单的测试存储库,其中一次提交包含两个文件 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 中,但该列表是动态生成的。我可以自动更改文件,但直到另一次提交后更改才会被拾取。

是否有其他一些不需要再次提交即可运行的调用?

您可以创建一个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 参数长度以下

我认为您几乎做到了:可以从多个位置读取属性,.gitattributes 只是其中最常见的位置。第二个——被认为是每个存储库的配置——是 $GIT_DIR/info/attributes.

引用手册:

Note that attributes are by default taken from the .gitattributes files in the tree that is being archived. If you want to tweak the way the output is generated after the fact (e.g. you committed without adding an appropriate export-ignore in its .gitattributes), adjust the checked out .gitattributes file as necessary and use --worktree-attributes option. Alternatively you can keep necessary attributes that should apply while archiving any tree in your $GIT_DIR/info/attributes file.

因此,如果可能的话,将您的列表粘贴到该文件,然后执行 git archive

另一种方法是不使用 git archive 而是仅 tar 工作树传递 tar 接受文件的 --exclude-from 命令行选项。这不适用于裸存储库,但如果您可以在归档之前检查内容,则可以通过 git read-treegit checkout-index 提供正确的 $GIT_INDEX_FILE 来完成和 $GIT_WORK_TREE 环境。变量。

另一种可能的解决方法是反转方法:tar(至少 GNU tar)支持一个鲜为人知的选项,即能够 删除 东西来自管道中的存档。

基本上可以做到

 $ tar -C a_path -c -f - . \
   | tar -f - --wildcards --delete '*.pdf' >result.tar

以便管道中的第一个 tar 归档所有内容,而第二个则传递所有内容,除了匹配 *.pdf shell glob patten 的文件。

因此,如果使用 shell globs 指定要删除的文件可以满足命令行限制,只需将 git archive 的输出通过管道传输到 tar 进程,从而删除这些内容不需要。

除了将 export-ignore 放入(已提交).gitattributes,您还可以将其放入(未提交)$GIT_DIR/info/attributes 文件。或者保留 .gitattributes 未提交并使用 --worktree-attributes 选项,这也可能不太好,因为它会使您的工作树变脏。

使用 Git 版本 2.20 (Windows) 和 Gitolite 服务器(未知版本)这对我来说可以排除名为 "b" 的文件和文件夹:

git archive HEAD . ":!b" | tar tf -

这也有效:

git archive HEAD . ":(exclude)b" | tar tf -

请注意,我在Windows平台上必须使用双引号,其他平台不确定。

一个可能的解决方案是git archive想要一个树状的归档。

您正在通过它 HEAD(可能是最常见的选择)。为了让它按照你的意思去做,这个 ref 会自动解析为它指向的对象——显然这将是一个提交。并且提交对象被解析为附加到它的树对象。所以你得到了当前提交的内容。到目前为止,很明显。

但是你可以传递任何你想要的树对象!这有什么帮助?好吧,您始终可以使用 git write-tree 从索引的当前状态创建一个树对象 - returns 它刚刚在 stdout 上创建的树对象的 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