camino
camino

Reputation: 10594

.gitignore what is the difference between subdir/* and subdir/?

test/test/build64/ALL_BUILD.vcxproj

The file above is ignored using following pattern:

build64/ 

But, the pattern below doesn't work.

build64/*

Upvotes: 3

Views: 59

Answers (1)

torek
torek

Reputation: 489073

The key difference is that build64 does not have a slash in it.

I know you actually wrote build64/ and build64/*, and build64/ does have a slash in it, but build64 does not have a slash in it and that's what makes all the difference.

Have a look at the gitignore documentation and scan down to the section headed with PATTERN FORMAT. Note the bullet point that begins with:

  • If the pattern ends with a slash, it is removed for the purpose of the following description ...

The rest of the text is, I think, very confusing—too confusing—but the key here is that build64/ has its ending slash removed first, so that it doesn't have a slash in it any more. So now, if Git encounters a file named test/test/build64/ALL_BUILD.vcxproj, Git breaks that name into four file name components, which are:

  • test (the first test in test/test);
  • test (the second test in test/test);
  • build64; and
  • ALL_BUILD.vcxproj.

Does any one of these four components match build64? Why yes, the third component does match build64. The original build64 had a suffix slash, so is that component a directory name rather than a file name? Yes, it is: so test/test/build64/ALL_BUILD.vcxproj is ignored by the rule build64/, which does not contain a slash except for that ending slash that does not count.

On the other hand, if you write build64/*, Git does not remove the slash, so the pattern includes a slash. Git breaks down the full name test/test/build64/ALL_BUILD.vcxproj exactly as before, but this time, it requires that all those name components match the complete pattern. Do all four of the name components match build64/*? Well, build64/* starts with build64, and the first name component is test. This does not match so the match as a whole immediately fails.

Note that if you were to write test/test/build64/* as your .gitignore line, Git would match test/test/build64/ALL_BUILD.vcxproj against test/test/build64/*. Now the first component test must match test—it does—and the second component must also match test, which of course it does as well. The third component must match build64 and the last component, ALL_BUILD.vcxproj, must match *. All of these requirements are met, so this would select the file to make Git shut up about untracked-ness.

You could also write **/build64/*, because ** matches any number of leading components. This would match the full name test/test/build64/ALL_BUILD.vcxproj, but also build64/file, fred/build64/wilma, and fred/wilma/build64/betty. Note that it would not match fred/wilma/build64/barney/betty because barney/betty has too many components—but if you match the directory fred/wilma/build64/barney, Git may never bother to look inside fred/wilma/build64/barney/ to find betty anyway.

In any case, it's important to remember that shell-style matching is done one file name component at a time, except for the ** matches. The "all must match" rule is triggered by a gitignore pattern that includes an embedded slash; otherwise only one component must match. Trailing slashes are, as the documentation almost says,

removed for the purpose of

deciding whether to use the "all must match" or "only one must match" rule.

Upvotes: 2

Related Questions