2

I recently came across a .gitignore file that started some newlines with ./. I had no idea what those ./s did, and I was very surprised to find out that this was both a) a seemingly novel question, and b) very hard to find answers for.

I've seen these related questions, but the function of ./ in gitignores seems to be different from that in the bash terminal.

My testing seems to indicate that ./ just doesn't do anything. Given a folder structure:

C:.
│   .gitignore
├───bar
│       bar0.txt
├───baz
│       baz0.txt
└───foo
        foo0.txt

And a .gitignore

# .gitignore
./foo
./foo/*
./foo/
bar
/baz

If I do git add -A and git status, only these files are tracked:

new file:   .gitignore
new file:   foo/foo0.txt

So, what is the purpose of ./?

Edit: I'm on Windows.

phlaxyr
  • 923
  • 1
  • 8
  • 22

1 Answers1

5

Remember that in both Windows (and DOS) and Unix (including macOS) a singular dot in a file-path . refers to the current-directory.

From the .gitignore documentation, the relevant points are:

  • The slash / is used as the directory separator. Separators may occur at the beginning, middle or end of the .gitignore search pattern.
  • If there is a separator at the beginning or middle (or both) of the pattern, then the pattern is relative to the directory level of the particular .gitignore file itself. Otherwise the pattern may also match at any level below the .gitignore level.

So:

  • foo

    • will match any file-or-directory named foo in the same directory and any descendant directories relative to the .gitignore file.
  • ./foo

    • will match only a file-or-directory named foo in the same directory as the .gitignore file.
    • I note that even though the git manual says it matches “files or directories” remember that this is not recursive - nor does it match files in that subfolder (nor other subdirectories) without the ** glob wildcard. I don’t know why the manual says “files or directories” when git itself doesn’t store any information relating to directories - only files.
  • /foo

    • will behave the same as ./foo.
  • ./foo/

    • will match only a directory named foo in the same directory as the .gitignore file.
  • /foo/

    • will behave the same as ./foo/.

As for why someone used ./ as a prefix instead of just /, I can only speculate, but some reasons might by:

  • They wanted to use a syntax that made it immediately clear to a reader unfamiliar with .gitignore's rules that the specified path is relative to the .gitignore file's directory, as /foo (without a leading .) could be interpreted as an absolute-path (that's either filesystem-absolute, especially on Linux, e.g. /usr/bin - or an absolute path inside the git repo)
  • They generated the .gitignore file by piping the output from another program or utility that used leading . in its output.
  • They themselves were unaware of the rules and this is just an example of Cargo Cult programming (i.e. doing things a particular way (especially when it's elaborate) without understanding why they're doing it that way, and without investigating if it's necessary).
Dai
  • 141,631
  • 28
  • 261
  • 374
  • That's what I thought what would happen, but with my test case `./foo` doesn't tell git to ignore the directory `foo` or "foo0.txt", like what I thought `/foo` would do (at least on my machine). – phlaxyr Jan 09 '21 at 14:20
  • @phlaxyr That’s right - because it isn’t using the syntax to exclude the contents of subdirectories recursively (that would be `/foo/**`). I admit my answer was incomplete because git does not store directories, only files. But it is interesting that the git manual implies it does. – Dai Jan 09 '21 at 15:04
  • @Dai: while Git doesn't *store* directories (because you can't get one into Git's index), it does *understand* them in the host-file-system tree-walking code, and the ignore stuff makes use of this to do shortcut exclusion of entire subtrees, to avoid lots of `lstat` calls. That's where most of the "doesn't store directories, but does know about them" internal conflict comes from. – torek Jan 09 '21 at 18:21
  • @Dai Yeah, but even when I put a file `foo/foo0.txt` in `foo/`, git adds `foo0.txt` even though i have `./foo` in the .gitignore. – phlaxyr Jan 09 '21 at 19:06
  • I put my updated test case [here](https://gist.github.com/phlaxyr/12954dca2a5ff0a251b6c0a9df4ae736) – phlaxyr Jan 09 '21 at 19:16