[英]Why .config directory is not listed when excluded from gitignore using wildcard (*) and git status?
I know the question is a bit cryptic, I couldn't word it exactly in a single sentence ( might need some help on that ).我知道这个问题有点神秘,我不能用一个句子准确地说出来(可能需要一些帮助)。
I initialized git in my home directory (ie, ~/
, on Arch Linux ) to backup my dot-files (mainly configs).我在我的主目录(即
~/
,在Arch Linux上)初始化了 git 以备份我的点文件(主要是配置)。 I want to include every file and folder in it except the ones starting with a .
我想在其中包含每个文件和文件夹,除了以
.
(like .config/
and .bashrc
). (如
.config/
和.bashrc
)。
So I made a .gitignore
file whose contents are:所以我制作了一个
.gitignore
文件,其内容是:
# Ignore everything
*
# Except these files and folders
!.*
But the problem is when I list all the untracked files ( git status
), it doesn't list the .config/
directory for some reason.但问题是当我列出所有未跟踪的文件(
git status
)时,由于某种原因它没有列出.config/
目录。 I tried playing around with .gitignore
and adding我尝试使用
.gitignore
并添加
!*/
shows all directories including .config/
and also Documents
, Downloads
etc, which I don't want to include.显示所有目录,包括
.config/
以及Documents
、 Downloads
等,我不想包括在内。
And instead adding而是添加
!.*/
shows every other directory that starts with a .
显示以 . 开头的所有其他目录
.
like .cache/
, .vim/
etc. But for some reason the .config/
doesn't show up.像
.cache/
, .vim/
等。但由于某种原因.config/
没有出现。
I even tried我什至试过
!.config/
and和
!.config
it doesn't work.它不起作用。 The only thing that works is
!*/
(all directories, which is not what I want)唯一有效的是
!*/
(所有目录,这不是我想要的)
Any way to solve this.有什么办法可以解决这个问题。 Its really annoying.
它真的很烦人。
[Kinda Solved]: edit: it was a bug [有点解决]:编辑: 这是一个错误
After some playing around I figured a way around it.玩了一圈之后,我想出了一个解决办法。 I don't know if I can call it exactly a solution but it works and this is how my
.gitignore
looks like:我不知道我是否可以将其称为完全解决方案,但它可以工作,这就是我的
.gitignore
的样子:
# Ignore everything
*
# Except these files and folders
!.*
!.*/*
And it works, when I run git status
it includes all files and directories starting with a .
它有效,当我运行
git status
时,它包括所有以.
including .config/
and some others which I didn't notice wasn't shown earlier along with .config/
.包括
.config/
和其他一些我没有注意到的,之前没有与.config/
一起显示。 Check my comment below if you wanna try to figure out (or help me to figure out) why this works.如果您想弄清楚(或帮助我弄清楚)为什么会这样,请查看下面的评论。
After the bug fix, you'll want what you put in your "kinda solved" section, or something similar.修复错误后,您将需要在“有点解决”部分中放入的内容或类似内容。 I think you'll want what I put in my "bottom line" section, really:
我想你会想要我放在“底线”部分的内容,真的:
/*
!/.*
As jthill noted in a comment , there is a bug in the .gitignore
wildcard handling in Git 2.34.0, which will be fixed in 2.34.1.正如jthill 在评论中指出的那样,Git 2.34.0 中的
.gitignore
通配符处理存在一个错误,该错误将在 2.34.1 中修复。 In this case I think the bug is making your wildcarding work better than it would otherwise, though.在这种情况下,我认为该错误使您的通配符工作比其他情况更好。
The first lines:第一行:
# Ignore everything
*
do just what they claim: ignore everything .做他们声称的事:忽略一切。 All files and folders (directories) are ignored.
所有文件和文件夹(目录)都被忽略。 Subsequent lines insert exceptions.
后续行插入异常。 But hang on a moment, what does ignored really mean?
但是等一下,忽略的真正含义是什么? To get there, we must note what Git's index (or staging area ) is and how Git makes new commits from the index / staging-area.
要到达那里,我们必须注意 Git 的索引(或暂存区域)是什么以及 Git 如何从索引/暂存区域进行新提交。
The index, or staging area, in Git, is a central and crucial concept. Git 中的索引或暂存区是一个中心且至关重要的概念。 Trying to use Git without understanding what the index is doing is a bit like trying to pilot an airplane without understanding what the wings and engine are for.
在不了解索引在做什么的情况下尝试使用 Git 有点像在不了解机翼和发动机用途的情况下尝试驾驶飞机。 1 So: the index is all about the next commit you plan to make .
1所以:索引是关于你计划进行的下一次提交。 If you never make any new commits, you don't really need to know about it, but if you do want to make new commits, you need to know this.
如果您从未进行任何新的提交,您实际上不需要知道它,但如果您确实想进行新的提交,您需要知道这一点。 2
2
When you first extract some commit, in order to use and work on it, Git fills in its index from that commit, so that the index contains all the files from that commit.当您第一次提取某个提交时,为了使用和处理它,Git从该提交中填写其索引,以便该索引包含该提交中的所有文件。 From this point onward, everything you are doing in your working tree, in the pursuit of making a new commit, is irrelevant to Git .
从现在开始,你在工作树中所做的一切,为了追求新的提交,都与 Git 无关。 That is, it's irrelevant up until you tell Git that you'd like Git to copy updated and/or new files into Git's index.
也就是说,在您告诉 Git 您希望 Git 将更新和/或新文件复制到 Git 索引之前,它是无关紧要的。
The git add
command is about updating Git's index. git add
命令是关于更新 Git 的索引。 The files you name to git add
, with git add file1 file2
for instance, are to be copied into Git's index.您命名为
git add
的文件,例如git add file1 file2
,将被复制到 Git 的索引中。 If there's already a copy of those two files, those copies get booted out of the index, replaced with the updated ones.如果已经有这两个文件的副本,则这些副本会从索引中启动,并替换为更新的副本。 If not, those files are newly added to the index.
如果没有,这些文件将新添加到索引中。
Once a file is in the index , you can replace it at any time: any .gitignore
entry is irrelevant at this point.一旦文件在 index中,您可以随时替换它:此时任何
.gitignore
条目都无关紧要。 You can also remove it from the index , with git rm
, or by using git add
after removing the working tree copy: either one will remove the index copy.您也可以使用
git rm
或使用git add
在删除工作树副本后将其从索引中删除:任何一个都将删除索引副本。 Now it's no longer in the index and the .gitignore
entries are back in play.现在它不再在索引中并且
.gitignore
条目重新发挥作用。
You can use an en-masse git add
, as in git add.
您可以使用
git add
,如git add.
or git add *
, 3 to have Git scan directories and files and add them for you.或
git add *
, 3让 Git扫描目录和文件并为您添加它们。 When you do this, Git will skip certain directories and/or files if it can, and this is an area where .gitignore
really comes into play.当你这样做时,Git 将跳过某些目录和/或文件,如果可以的话,这是
.gitignore
真正发挥作用的领域。
1 "Why should I care about those? I only care about getting my passengers and cargo from point A to point B, and those are inside the plane, not out on the wings." 1 “我为什么要关心这些?我只关心将我的乘客和货物从 A 点运送到 B 点,这些都在飞机内,而不是机翼外。”
2 To extend the plane analogy a bit more: if you're just planning to use the fuselage as a house, then indeed, you don't need to care about the engines and wings. 2再把飞机类比扩展一点:如果你只是打算把机身当房子用,那么实际上,你不需要关心发动机和机翼。
3 Note that in Unix-like shells, git add *
is quite different from git add.
3请注意,在类 Unix shell 中,
git add *
与git add.
because the shell will expand *
for Git: Git never sees the literal asterisk.因为shell将扩展
*
对于 Git:Git 永远不会看到文字星号。 When the shell expands *
, it does so with dot-files excluded, by default at least (bash in particular has a control knob to change this behavior).当 shell 扩展
*
时,它会排除点文件,至少默认情况下(特别是 bash 有一个控制旋钮来更改此行为)。 In some CLIs, the literal asterisk *
gets through to Git, and then Git will expand *
, and now it can act like git add.
在某些 CLI 中,文字星号
*
会传递到 Git,然后Git将扩展*
,现在它可以像git add.
if Git wants it to.如果 Git 想要它。 But it's easier to type in
git add.
但是输入
git add.
(no SHIFT key required) so that's what I always do anyway, which removes the difference in the first place. (不需要SHIFT键)所以无论如何我总是这样做,这首先消除了差异。
If you run git add.
如果你运行
git add.
or equivalent (see footnote 3 again), Git will:或等效(再次参见脚注 3),Git 将:
.
.
. .gitignore
file at this level, adding (appending) these rules to the ignore rules..gitignore
文件,将这些规则添加(附加)到忽略规则中。 (These rules then get dropped when we finish this directory.) !
!
开头的规则。 . Git finds the last applicable rule , whatever that is, in the current set of rules, and then obeys that rule. Git 在当前规则集中找到最后适用的规则,无论是什么,然后遵守该规则。 So first, let's define which rules apply to which directory-scan results, and then what the various rules do.
所以首先,让我们定义哪些规则适用于哪些目录扫描结果,然后是各种规则的作用。
A rule in a .gitignore
can be: .gitignore
中的规则可以是:
generated.file
;generated.file
;somedir/
;somedir/
;/foo
, a/b
, /foo/
, a/b/
, and so on;/foo
、 a/b
、 /foo/
、 a/b/
等; or These can all be negated: if a rule starts with !
这些都可以否定:如果规则以
!
it's negated, and we strip off the !
它被否定了,我们去掉了
!
and then use the remaining tests.然后使用剩余的测试。 The two keys tests are these:
两个关键测试是:
/
?/
结尾? If so, it applies only to directories / folders./
character?/
字符? (The one at the end does not count here.) If so, this entry is anchored or rooted (I like the term anchored myself, but I've seen both terms used). An anchored entry matches only a file or folder name found at this level .锚定条目仅匹配在此级别找到的文件或文件夹名称。 That is,
/foo
or foo/bar
won't match sub/foo
or sub/foo/bar
, only ./foo
and ./foo/bar
, where .
也就是说,
/foo
或foo/bar
不会匹配sub/foo
或sub/foo/bar
,只有./foo
和./foo/bar
,其中.
is the directory (folder) that Git is scanning right now .是 Git现在正在扫描的目录(文件夹)。 This means that if the entry has several levels—
foo/bar
or one/two/three
for instance—Git will have to remember to apply this entry when it gets around to scanning bar
in foo
, or two
in one
and three
in one/two
.这意味着如果条目有多个级别——例如
foo/bar
或one/two/three
——Git 必须记住在扫描foo
中的bar
时应用此条目,或者two
one
three
one/two
。 So we do have to consider "higher level" rules.所以我们必须考虑“更高级别”的规则。 But since lower level rules get appended , a lower level
.gitignore
can cancel out the higher level one if it wants to.但是由于附加了较低级别的规则,因此如果需要,较低级别的
.gitignore
可以取消较高级别的规则。
An un-anchored entry applies here and—unless overridden—in every sub-directory as well .未锚定的条目适用于此处,并且(除非被覆盖)也适用于每个子目录。 That is, if we do have
./one/two/three
, Git will presumably open and read one
to find two
, and then open and read two
to find three
, all while still working on the current directory .也就是说,如果我们确实有
./one/two/three
,Git 可能会打开并读取one
找到two
,然后打开并读取two
找到three
,同时仍在当前目录上工作。 Meanwhile any un-anchored entry from this .gitignore
will apply within the one
and one/two
directories, and within one/two/three
if that's a directory, and so on.同时,此
.gitignore
中的任何未锚定条目都将适用于one
和one/two
目录,如果是目录,则适用于one/two/three
,依此类推。
So, there's already a lot to think about.所以,已经有很多事情要考虑了。 Now we throw in glob matches.
现在我们加入全局匹配。
The usual glob is *
: people write foo*bar
or *.pyc
or whatever.通常的 glob 是
*
:人们写foo*bar
或*.pyc
或其他什么。 Git allows **
as well, with meaning similar to that in bash: zero or more directories. Git 也允许
**
,含义类似于 bash:零个或多个目录。 (I've found **
in Git to be weird and in my opinion slightly buggy, where it sometimes seems to mean "one or more" instead of "zero or more", so I recommend avoiding **
if possible. It's hard to reason about, so it's generally not a great idea in the first place, and Git's ignore rules mostly eliminate any need for **
. So if you are going to use it, test it carefully and be prepared to have it shift on you in some future Git, in case the one-or-more?bug? gets fixed, or affects your use case, or whatever.) (我发现 Git 中的
**
很奇怪,而且在我看来有点错误,有时它似乎意味着“一个或多个”而不是“零个或多个”,所以我建议尽可能避免使用**
。很难原因,所以这通常不是一个好主意,而且 Git 的忽略规则基本上消除了对**
的任何需要。所以如果你要使用它,请仔细测试它并准备好让它在某些时候转移到你身上未来的 Git,以防一个或多个?错误?得到修复,或影响您的用例,或其他。)
Let's suppose, then, that we have these two entries:那么,假设我们有这两个条目:
*
!.*
Git opens and reads .
Git 打开并读取
.
and finds the following names:并找到以下名称:
dir
file
.dir
.file
where dir
and .dir
are directories (folders) and file
and .file
are non-directories (files).其中
dir
和.dir
是目录(文件夹),而file
和.file
是非目录(文件)。
The *
rule matches all four names. *
规则匹配所有四个名称。 The ..*
rule matches the last two names. ..*
规则匹配最后两个名称。 The ..*
rule is later in the .gitignore
file, so it overrides the *
rule. ..*
规则稍后在.gitignore
文件中,因此它覆盖了*
规则。 Git therefore "sees" .dir
and .file
. Git 因此“看到”
.dir
和.file
。
Since .file
is a file, this means that git add.
由于
.file
是一个文件,这意味着git add.
"sees" it. “看到它。 It will check whether
.file
needs to be git add
-ed to displace the existing .file
file, or added to the index.它将检查
.file
是否需要被git add
-ed 以替换现有的.file
文件,或添加到索引中。
Since dir
and file
are excluded, this scanning pass doesn't see them, and does not try to git add
either one.由于
dir
和file
被排除在外,因此此扫描过程看不到它们,并且不会尝试将git add
任何一个。 Since dir
itself is a directory (not a file), it's never in the index itself.由于
dir
本身是一个目录(不是文件),因此它永远不会在索引本身中。 There may be a file in the index named dir/thing
, and Git will check to see if that should be updated by this git add.
索引中可能有一个名为
dir/thing
的文件,Git 将检查该文件是否应由此git add.
, but Git won't scan dir
to see if there are other files in dir
. , 但 Git 不会扫描
dir
以查看dir
中是否还有其他文件。
Since file
is an excluded file, the scanning pass does not see it.由于
file
是排除文件,因此扫描过程看不到它。 But if file
already exists in the index, Git will check to see if it should be updated by this git add.
但是如果
file
已经存在于索引中,Git 将检查它是否应该被这个git add.
, even though it didn't get scanned here. ,即使它没有在这里被扫描。 In other words, these "existing files already in the index" checks happen outside (either before or after) the "scan the directories" pass.
换句话说,这些“已存在于索引中的文件”检查发生在“扫描目录”通道之外(之前或之后)。
Meanwhile, since .dir
isn't excluded, Git now opens and reads .dir
, recursively:同时,由于不排除
.dir
,因此 Git 现在打开并递归读取.dir
:
.dir/.gitignore
(the .gitignore
that applies to entries found in .dir
). .dir/.gitignore
(适用于.dir
中的条目的.gitignore
)。 If that exists, Git appends those rules..dir
recursively, using all the same methods. .dir
。 Then it's done scanning .dir
so Git removes the appended rules..dir
所以 Git 删除附加的规则。 Let's look now at the rules Git has in effect as it scans .dir
.现在让我们看看 Git 在扫描
.dir
时生效的规则。
If there is a .dir/.gitignore
, Git opens and reads it and appends to the existing rules.如果有
.dir/.gitignore
, Git 打开并读取它并附加到现有规则。 If not, we still have the same set of rules in effect:如果没有,我们仍然有相同的规则集:
* (positive wildcard: ignore every name)
!.* (negative wildcard: don't ignore dot-names)
What's in .dir
? .dir
中有什么? Let's say we have:假设我们有:
file1
dir1
.file2
.dir2
The name file1
matches *
so it gets ignored .名称
file1
与*
匹配,因此被忽略。 Git won't git add
it to the index if it's not already there. Git 不会
git add
到索引中。 Similarly, dir1
matches *
, so it gets ignored .同样,
dir1
匹配*
,因此它被忽略。 Git won't even scan it to see if there are any files there. Git 甚至不会扫描它以查看那里是否有任何文件。
The name .file2
matches *
, but also matches .*
, so the override negative entry is the rule that applies: Git will git add.dir/.file2
.名称
.file2
匹配*
,但也匹配.*
,因此覆盖否定条目是适用的规则: Git 将git add.dir/.file2
。 The name .dir2
has the same features, so the override applies and Git will open and read .dir/.dir2
.名称
.dir2
具有相同的功能,因此应用覆盖并且 Git 将打开并读取.dir/.dir2
。 This goes through the same recursion as before: Git looks for .dir/.dir2/.gitignore
to append rules, and will use the appended-to rules while scanning .dir/.dir2
, and then drop back to our own .dir/.gitignore
-appended rule set while continuing to scan .dir
, and then return from this recursion level and drop the .dir/.gitignore
rules.这经历了与以前相同的递归: Git 查找
.dir/.dir2/.gitignore
到append规则,并在扫描.dir/.dir2
时使用附加到规则,然后返回到我们自己的.dir/.gitignore
附加规则集,同时继续扫描.dir
,然后从该递归级别返回并删除.dir/.gitignore
规则。
In the end, the trick here is that we want the *
rule to apply only at the top level .最后,这里的诀窍是我们希望
*
规则仅适用于顶层。 Once we get into, say, .foo/
, we don't want to ignore .foo/main_config
and .foo/secondary_config
.一旦我们进入
.foo/
,我们不想忽略.foo/main_config
和.foo/secondary_config
。 So we want *
to apply only at the top level .所以我们希望
*
只在顶层应用。
Using:使用:
# Ignore everything
*
# Except these files and folders
!.*
!.*/*
gets us closer: we ignore everything, but then—via the negative rules ..*
and ..*/*
—we carefully don't ignore .foo
and the like.让我们更接近:我们忽略所有内容,但随后——通过否定规则
..*
和..*/*
——我们小心地不要忽略.foo
等。 Once we get into .foo
, we carefully don't ignore .foo/main_config
.一旦我们进入
.foo
,我们小心不要忽略.foo/main_config
。
The bug, or possible bug, depending on what you really do want, here is... well, suppose we have .foo/thing1/config
and .foo/thing2/config
.错误或可能的错误,取决于您真正想要的,这里是......好吧,假设我们有
.foo/thing1/config
和.foo/thing2/config
。 The .*/*
pattern contains an embedded slash , which means it is anchored . .*/*
模式包含一个嵌入的斜线,这意味着它是锚定的。 It matches .foo/thing1
, so that directory gets scanned.它匹配
.foo/thing1
,因此该目录被扫描。 But it doesn't match .foo/thing1/config
.但它不匹配
.foo/thing1/config
。
We could try something like:我们可以尝试类似:
!.*/*
!.*/**/
I particularly hate this one because **
is so tough to reason about.我特别讨厌这个,因为
**
很难推理。 We could also write:我们也可以这样写:
!.*/*
!.*/*/
!.*/**/
in case the **
"one or more" bug bites us (I don't think it will, but it's a consideration).以防
**
“一个或多个”错误咬我们(我认为不会,但这是一个考虑因素)。 But it's simplest to anchor the original globs , by writing:但是最简单的锚定原始 glob的方法是:
/*
!/.*
This makes the top level .gitignore
rules apply only to top-level work-tree entries .这使得顶级
.gitignore
规则仅适用于顶级工作树条目。 Sub-level .gitignore
files, if they exist, can establish sub-level rules and do not need to override any top-level rules, because the top-level rules already don't apply at any sub-level , thanks to anchoring.子级
.gitignore
文件,如果存在,可以建立子级规则,不需要覆盖任何顶级规则,因为顶级规则已经不适用于任何子级,这要归功于锚定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.