18

hello/ ignores all folders named "hello" anywhere in my folder structure

hello/* only ignores the folder "hello" at the top-level.

Why is this? Please point to a passage in http://git-scm.com/docs/gitignore that explains this behavior.

stackoverflowuser
  • 1,157
  • 9
  • 18

2 Answers2

15

This may seem stange, but this allows us to match directories in multiple ways: files or directories anywhere, directories anywhere, or directories at the top level. This versatility is very useful, and can prevent having a cluttered .gitignore.

(FYI, if you have Git 1.8.2+, you can use git check-ignore to help debug this.)


If you put foo, it will match all files and directories named foo.


If you put foo/, it will only match directories named foo.

If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in Git).


If you add *, as in foo/*, it is treated as a file glob (relative to the .gitignore).

Otherwise, Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, "Documentation/*.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".

Thus for foo/*, Git will ignore all files and directories in the top-level directory foo. It will ignore foo/dir, foo/file.txt, etc. (Technically, this will not ignore foo itself, but it will ignore its children. However, since Git does not track directories, it has the same effect.)

FYI, foo/** would have the same behavior.


My recommendation:

If you want to ignore a directory foo at the top level, IMO it is most clear to use this rule:

A leading slash matches the beginning of the pathname. For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".

So you can write /foo/, and it will ignore the directory foo at the top-level, but nowhere else.

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 1
    I really loved your earlier explanation that you edited out: "The reason for this: This allows users to match directories in three ways: files or directories anywhere, directories anywhere, or directories at the top level." Very nice simple description of the purpose of it all :) – stackoverflowuser May 25 '14 at 16:16
  • @stackoverflowuser, I was trying to edit for brevity. I put it back in. Thanks :) – Paul Draper May 25 '14 at 16:20
8

The relevant passage is:

Git treats the pattern as a shell glob suitable for consumption by fnmatch, with:

  • '*' for the top files
  • '**' everything inside, with infinite depth

fnmatch is a function which checks whether the string argument matches the pattern argument, which is a shell wildcard pattern.

That is useful when excluding subfolders from an ignore rule, as I mention in "How to INCLUDE lib files inside [/Libs/x64/Release] folder in a Git repository"

Libs/**/*
!Libs/x64/Release/

That ignores everything except Libs/x64/Release folder.

In any case, the command git check-ignore -v is very useful to check which .gitignore rule applies for any particular file.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250