The difference between a/ and a/* and a/** in .git

2019-07-16 01:25发布

Here is my file folder.

  • a
    • b
      • b1.txt
      • b2.txt
    • a1.txt
    • a2.txt
  • .gitignore

Firstly:

I found if I wanna ignore the folder a, a/ and a/* and a/** all can complete.

Secondly:

I wanna ignore all the things in folder a except the folder b, the only way to do this is :

a/*
!a/b/

I don't know why a/** can't do this?

# this way can't except folder b
a/**
!a/b/

Or you can tell me how can I use a/** ?

标签: git gitignore
3条回答
你好瞎i
2楼-- · 2019-07-16 01:43

To a first approximation, Git doesn't "do" directories ("folders") at all.

Gitignore files are an exception to this rule, but in a tricky way.

First, Git is mainly about files. Files are either tracked or untracked. A file is untracked if and only if it is not currently in the index. Note that Git can read its own index very quickly, usually much faster than the underlying operating system can read through directory trees ("folders and subfolders").

Next, gitignore rules are mainly about files, since only files can be untracked or become tracked. When git status says that some directory ("folder") is untracked, what it means is that there are one or more untracked files contained within that directory. (Using git add works similarly, except that instead of "complain about", the action to be taken is "copy into index".)

In order to find untracked files, Git would have to open and read every directory (or "folder") in your work-tree. To make Git perform faster, however, Git uses this trick:

  • if the path P (such as a or a/b) names a directory, and
  • if there are no existing tracked files whose path begins with P (followed by a trailing slash; e.g., in this case, there is no a/b/b1.txt and no a/a2.txt in the index, for instance), and
  • if path P matches an "ignore" rule (without a cancellation for it), then

Git skips reading the directory P entirely. It never looks to see if there are any untracked files within it. It does this because reading directories in Linux (and other OSes) is relatively slow.

So now we come to a pattern like a/* or a/**, where either a is currently not ignored, or there are files in the index whose paths start with a/. In order to decide whether there are files in a that are untracked and should be complained-about (if not ignored) or not-complained-about (if ignored), Git must read directory a.

Having read directory a, Git finds that there are two files a1.txt and a2.txt and a directory b. Assuming a1.txt and a2.txt are currently untracked, it scans all .gitignore patterns to see if those match the two files. If not, Git complains about them (or adds them to the index). But b is a directory, so the optimization kicks in:

  • there are no b/ paths in the index, and
  • b matches the a/* or a/** rule

As long as there is no !a/b or !a/b/ rule, Git will ignore directory a/b at this point and never look inside it. It does not matter whether you use * or **.

Should there be at least one tracked file in the index whose name starts with a/b/, however, the optimization fails. Or, if you override the ignore with !a/b or !a/b/, the directory itself is not ignored. In either of these cases, Git reads directory a/b/. Having read the directory, Git will find files a/b/b1.txt and a/b/b2.txt. It then checks whether these match a .gitignore rule.

As Vampire noted, ** matches zero or more directories. (If the pattern ends with ** it also matches all files in those zero-or-more directories.) To make this have some action that differs from a/*, so that we could see a difference, we would need additional levels of sub-directories (which we would need to force Git to read, either by explicitly un-ignoring at least one such as a/b/ with ! rules, or by having tracked files within them).

(In experiments I ran a while ago, I found cases where ** matches one or more directories in Git, instead of zero or more. I think this occurs in pathname expansion, e.g., if you use git log -- 'a/**/file.txt' for instance. So watch out: the ignore rules for ** matching may differ from other rules.)

查看更多
贪生不怕死
3楼-- · 2019-07-16 01:47

** matches any amount of directories, * only one.
So if you e. g. ignore /a/**/d/, then /a/b/d/ and /a/b/c/d/ both are ignored.
If you e. g. ignore /a/*/d/, then /a/b/d/ is ignored while /a/b/c/d/ is not ignored.

If a folder is ignored, then Git does not even look at its contents to check for inclusions. That is why you cannot ignore /a/ and then include /a/b/, as Git does not even look into /a/. Because of this you must instead ignore all contents in /a/ with /a/* and then include /a/b/ to ignore everything in /a/, except all contents of /a/b/.

查看更多
再贱就再见
4楼-- · 2019-07-16 02:03

try (without /):

a
!a/b

** is used in ignoring "in any subdirectory", for example, you have

a/b1.txt
a/b/b1.txt

you can ignore all b1.txt files in any subdirectories with a/**/b1.txt

查看更多
登录 后发表回答