我怎样才能排除除所有顶级点文件和子目录之外的所有内容?

How can I exclude everything but all top-level dot files and a subdirectory?

在使用 Git 跟踪我的基本配置文件时,我在编写 .gitignore 文件时遇到了问题。

我的目标是在我的存储库中包含两件事:

  1. 我的用户目录 ~/ 中的所有点文件,这是我的 gitroot。
  2. 隐藏文件夹~/.vim/bundle

到目前为止,我已经尝试了以下方法,但无济于事。

.gitignore:

# Ignore everything
*

# Include top-level dot files.
!/.*

# Include vim plugins
!/.vim/bundle/*

这只会在我的 gitroot 中暂存点文件。

注.

我的gitroot是我的用户目录~/.

更新

在下面的表格中,~/.vim文件夹暂存的:

# Ignore everything
*

# Include top-level dot files.
!/.*

# Include vim plugins
!/.vim

所以,我猜我应该以某种方式表达路径名 /.vim/bundle

前导斜杠表示相对于gitroot的绝对路径;将其删除到 select 所有点文件。

# Ignore everything
*

# Include all dot files.
!.*

# Include vim plugins
!/.vim/bundle/*

Here is 带有示例的方便简洁的参考。

您需要暂存然后提交 .gitignore 以使更改生效。令人高兴的是,您可以重复 git commit --amend 进行进一步的更改,以保持历史记录干净,直到您拥有想要的内容。

作为一个模糊相关的旁白,我经常看到人们试图将 "a folder of name folder anywhere in the project" 与 /folder 匹配,而应该是 **/folder.

我受到 this answer 的启发,编写了以下足够的 .gitignore 文件。 :)

# We use a whitelisting approach to track certain configuration files.
# Apparently, this needs to be done recursively because git can't see past a directory that is not
# included. Hence, if we want to include a subdirectory, we first need to include its upper
# directory, by the whitelisting procedure as shown below.

# Exclude all files and non-hidden directories.
/*

# Include all dotfiles.
!.*

# Exclude all hidden directories.
.*/

# Include the upper directory.
!/.vim

# Exclude all files and non-hidden directories.
/.vim/*

# Include the vim plugins.
!/.vim/bundle

TL;DR

您正在 运行 进行优化,这会破坏您的用例:一旦 Git 决定不查看目录内部,它永远不会在 [=] 中找到 文件116=] 它将添加的那个目录。要修复它,您至少需要一个规则:

!/.vim/bundle/

您可以使用不同的规则来击败所有优化:

!*/

这也应该可以解决问题(以更深入的搜索为代价,这会导致速度很慢)。

你有一个好的开始,但是你 运行 进入了一个有问题的优化(我认为 Git 的人正在尝试修复)。我们还必须假设您的 index(也称为临时区域)中当前的内容,因为索引内容会影响 Git 扫描目录的方式。让我们假设您的索引确实是空的,因为它会在 git init-ing 一个新的存储库之后。这样,由于不存在现有索引条目,它们不会影响 Git 的正常目录扫描过程。

假设您的工作树包含名为 README.profile 的文件,以及名为 d/.vim/ 的目录。您的 .gitignore 有三个规则。第一个是肯定规则 ("do ignore"),第二个和第三个是否定规则 ("don't ignore")。规则本身减去 ! 是:

*
/.*
/.vim/bundle/*

现在,当 Git 处理 README 时,它首先检查匹配的 *,然后检查不匹配的 /.*,然后检查 /.vim/bundle/*这也不匹配。最后一个匹配规则生效:即 * 表示 "do ignore" 所以 README 将被忽略。另一方面,文件 .profile 是前两条规则,所以第二条适用,它说 ! 所以 .profile 未被跟踪但不会被忽略:git status 将发牢骚,git add . 会将其添加到索引中。

现在让我们考虑如何处理 d/.vim/。这些是目录,而不是文件:Git 不会保存它们,但它可能会或可能不会保存 中的文件。为此,它必须读取并检查其中的所有文件名。这非常昂贵,因此 Git 将首先尝试 跳过 整个目录。这就是我提到的有问题的优化。

无论如何,d/ 匹配第一个规则,但不匹配其他两个。这意味着 Git 可以 完全跳过阅读 d/ git status 不会抱怨它并且 git add 不会扫描它。

到目前为止我们都很好。 .vim/ 匹配第二条规则(但不是第三条)所以 Git 会在 .vim.

里面查找

假设 .vim/ 包含 .netrwhistafter/bundle/,也许还有一些其他目录。 .netrwhist 匹配 *,不匹配 /.*,不匹配 /.vim/bundle/*,因此应用 * 匹配并被忽略。 after/ 目录匹配 *,不匹配 /.*,也不匹配 /.vim/bundle/*,因此未读。

现在我们遇到了问题:.vim/bundle/ 是一个目录。 Git 根据您的 .gitignore 指令检查此路径名。 bundle/ 匹配 *.vim/bundle/.* 不匹配,因为这意味着 顶级文件 .vim/bundle/ 是目录,不在顶层。该路径也不匹配第三个指令 /.vim/bundle/*,它要求这是 .vim/bundle/ 内的文件或目录。所以 唯一匹配的指令是第一个,而 .vim/bundle 未读 就像 d/ 一样。

这意味着 Git 从未在 .vim/bundle/ 中找到任何文件或目录 。它从不根据任何内容检查文件(如果有的话),也从不扫描子目录以查找更多文件。您需要强制 Git 查看 .vim/bundle/.

索引中的条目变化很大

如果索引中有一些路径名为.vim/bundle/a/b的文件,Git将不得不扫描目录.vim/bundle/a。所以放置在那里的任何 new 文件都会显示出来!我不确定在这种情况下 Git 是否会跳过 .vim/bundle/ 本身(理论上它可以,但我认为实际上不会)。一旦您在索引中有了文件路径,该文件就会被 跟踪 并且它在任何 .gitignore 中的存在变得无关紧要。

但是请注意,索引 now 中的文件不一定在索引 tomorrow 中,如果您删除它们或如果您检查其他一些不存在这些文件的提交。整个事情有点棘手。