Reputation: 5841
I had static/
in the .gitignore
, which successfully ignored the entire directory. I now want to stage everything in it except static/vendor
.
I thought that having static/vendor/
inside .gitignore
would do it. However, it does not. When I submit static
for staging, all files from inside static/vendor
still end up being staged alongside others that I actually want.
Is there a trick to this? What am I missing?
Upvotes: 1
Views: 77
Reputation: 490078
The .gitignore
rules are a little weird. (For that matter, the name .gitignore
is a little weird, but forgivable: a better name would be .git-do-not-complain-about-some-files-that-are-untracked-and-do-not-automatically-add-them-in-that-case-but-this-file-has-no-effect-on-files-that-are-tracked
, or something equally unweildy.)
Each line in .gitignore
has one of a few general forms:
!
are do-not-ignore rules; andThis lets us dispense with the first type entirely, and think about the second and third type of line using one set of rules, with the "do not ignore" applied at the end. So now we're down to the following sub-rules:
foo
, or/leading
, ortrailing/
, or/leadandtrail/
, orhas/slash
, /has/slash
, has/slash/
, and so on.(Lines can also contain various glob characters, which for now, let's not worry about!)
To keep this explosion of sub-categories simpler, we want to group them into just two crucial sub-categories, which I will call anchored and un-anchored. Along a separate axis, we want to group them into two other crucial sub-categories, which I will call slash-suffixed and not-slash-suffixed.
An anchored name is any name that either starts with a slash or contains an embedded slash (or both). So /leading
and has/slash
and /has/slash
are all anchored, but foo
and trailing/
are un-anchored.
A slash-suffixed name is any name that ends in slash. This one final slash is removed as soon as the name is categorized as slash-suffixed. So trailing/
is slash-suffixed but then treated as if it were just spelled trailing
, and has/slash/
is also slash-suffixed and treated as if it were just spelled has/slash
.
Hence the entry:
vendor/
is slash-suffixed but un-anchored, while:
static/vendor/
is both slash-suffixed and anchored.
(A name like a/b//
is slash-suffixed and anchored and treated like a/b/
, and a name like b//
is slash-suffixed and anchored and treated like b/
. The extra slash left over is just a bad idea: don't do it. I'm not sure what actually happens!)
Now that we've categorized the names as anchored or un-anchored and slash-suffixed or not-slash-suffixed, we can describe how the gitignore rules work for them:
Any slash-suffixed name matches a partial path name if and only if that partial path name represents a directory/folder. So vendor/
matches the directory (or folder) vendor
, but not a file whose name component is vendor
. Without the slash-suffix, the name matches both a file and a directory.
Any anchored name matches only the components of file name that start from the same directory in which the .gitignore
file was found. That is, given an anchored vendor
, we'll exclude vendor
in this directory, but not a/vendor
, b/vendor
, and so on. Given an unanchored vendor, we'll exclude vendor
in this directory and a/vendor
and b/vendor
and so on.
This is why adding static/
in front of your vendor/
changed its behavior: it went from un-anchored to anchored, so that it began to act like /static/vendor/
(which would of course do the same thing: it's anchored in two ways, but the only thing that matters is that it is anchored).
Adding **/
in front enables the (still-anchored!) string static/vendor/
to match a/static/vendor
, b/static/vendor
, and so on. You didn't need the **/
in front when vendor/
was un-anchored.
Note, by the way, that you could remove the Edit: Oops, this was backwards: you would want a vendor/
entry entirely from this .gitignore
file (wherever it is) and create a static/
entry in vendor/.gitignore
, creating that file if necessary. That would be un-anchored but slash-suffixed. It would match the directory static
in that directory or any sub-directory of that directory.vendor/
in the static/
directory. But the general principle applies here.
(The main, or perhaps only, drawback to this is that you will have created or altered a file in this particular sub-directory—and, of course, that you might need to do it in multiple sub-directories.)
Upvotes: 4