Difference between .gitignore rules with and without trailing slash like /dir and /dir/

GitGitignore

Git Problem Overview


Is there a difference between /dir and /dir/ in the .gitignore file within a Git repository?

How are the following different?

/dir
/dir/
/dir/*

Git Solutions


Solution 1 - Git

This is a old question, but high ranked on Google and the top voted answer is wrong. Here goes the correct answer.

Yes, these rules are different.

  • /dir will match a file, directory, link, anything named dir
  • /dir/ will match only a directory named dir
  • /dir/* will match all files, directories and anything else inside a directory named dir (but not the dir directory itself).

/dir, /dir/ and /dir/* are NOT equivalent. The difference is very clear when using overriding rules, like the famous !.gitkeep to get around the limitation of tracking empty directories. Suppose the existence of the file dir/.gitkeep

  • With /dir and /dir/, Git won't even look inside the directory so the .gitkeep won't be seen.
  • With /dir/*, the file will be detected by Git and the directory will be kept if this .gitkeep is committed, because the rule doesn't apply to the directory itself, only to its contents.

OBSERVATION: All the rules mentioned above are anchored at the current directory (the place where the .gitignore is), because of the / prefix. Without the prefix, the rules would apply not only for that specific directory, but also for the sub-directories or everywhere in the repository, if the .gitignore is located at the root level.

Solution 2 - Git

The Patterns Have Different Meanings

According to the Pattern Format section of gitignore(5):

>- 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 the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the toplevel of the work tree if not from a .gitignore file).

What this means is that "dir" can be a file, directory, or symbolic link, but "dir/" with a trailing slash will only match a directory. In most cases, the difference won't matter, but when it does, understanding the distinction can remove ambiguity from your .gitignore files.

Solution 3 - Git

Git 2.23 (Q3 2019) attempts to revamp the description about slashes in gitignore patterns (used to indicate things like "anchored to this level only" and "only matches directories")

The documentation now includes:

> - 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.

> - If there is a separator at the end of the pattern then the pattern will only match directories, otherwise the pattern can match both files and directories.

> - For example, a pattern doc/frotz/ matches doc/frotz directory, but not a/doc/frotz directory;
however frotz/ matches frotz and a/frotz that is a directory (all paths are relative from the .gitignore file).

> - An asterisk "*" matches anything except a slash.
The character "?" matches any one character except "/".
The range notation, e.g. [a-zA-Z], can be used to match one of the characters in a range.
See fnmatch(3) and the FNM_PATHNAME flag for a more detailed description.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSrikanth ADView Question on Stackoverflow
Solution 1 - GitVictor SchröderView Answer on Stackoverflow
Solution 2 - GitTodd A. JacobsView Answer on Stackoverflow
Solution 3 - GitVonCView Answer on Stackoverflow