The answer is a little complicated because Git's scanning algorithm is tricky, but it boils down to the fact that *
matches lib/custom-file.el
but /*
does not.
As a general rule, in glob patterns, *
does not "cross" path name separator /
-es. Hence A*B
matches ANameThatEndsInB
but not ADirectory/WithAFileB
.
Putting a /
in a glob pattern requires that the thing before the /
name a directory and the thing after the /
occur within the directory: A*/*B
does match ADirectory/WithAFileB
. The name components, ADirectory
and WithAFileB
, are matched individually against the glob components, A*
and *B
.
A glob pattern ending in /
, as in */
, matches only directories. One starting with /
has another special meaning in Git (but note that, e.g., the .gitignore
entry /foo/bar/
consists of two components: /foo/
and then bar/
; the bar/
part does not start with a slash). In particular, if the pattern starts with /
, it applies only to files in the top level of that Git directory. If that's the top level .gitignore
then this means the root of the work-tree itself.
Now, as in Mad Physicist's answer, it's true that if Git has no existing tracked files within a directory, Git does not have to read that directory sometimes. An ignore pattern that matches a directory name will make Git skip the scanning of the directory, if Git does not already have to scan that directory for some other reason. So in general, if you have a .gitignore
that has *
in it anywhere, any directory tends to get skipped entirely—well, as long as Git doesn't already have to look inside it for some other reason. So *
in a .gitignore
tends to ignore all directories. However, Git's ignore rules say that whenever Git has hold of a file or directory name and is checking .gitignore
files, it should read through the entire file and find all matches, including the ones with !
in front of them. This means that, for instance:
*
!*/
will tell Git to ignore everything (including directories), but override that by saying don't ignore the */
directories. But the *
will continue to ignore all files.
If you make it read:
/*
!/*/
you'll ignore all files in this directory (the one containing the .gitignore
) but have Git scan all its subdirectories. Everything within any sub-directory such as foo/name
will fail to match either of these glob patterns, since the foo/name
part has a slash and the *
cannot cross it.