# The Straightforward Guide to Gitignore Patterns # Introduction Git's primary job is to hep us track changes to the files in our repository. Towards that end, Git dutifully and repeatedly informs us about untracked files to help remind us to add them with with a `git add` command. But there are many files we don't need or want to track and wish to exclude from our repository. We want Git to ignore these files so Git will stop reminding us about their existence. This helps eliminate noise from Git's reports, allowing us to focus on more important matters. More significantly, ignored files will not be added to the repository when we issue a `git add .` command. Examples of files we might want to ignore include: * those automatically generated by the OS * log files and build files * files containing sensitive information like passwords or API keys * configuration files that are specific to our development environment Git provides us with the "gitignore" feature for ignoring such files. Think of it as a "Do Not Track" list for the files in your project. The primary way for adding files to the list is to create a text file named ".gitignore" in the root directory of the repository. Inside this file we add patterns, one per line. Git calculates which files to ignore from the patterns we add to the .gitignore file. Note that while it's possible to create multiple .gitignore files in a repository, it's best practice to keep a single .gitignore file in the root directory to keep things simple. And so except where otherwise noted, **this guide assumes you have precisely one .gitingore file and it is placed in the root directory of your repository.** This will also help keep this guide focused on the topic at hand, gitingore patterns. # Using Patterns in the .gitignore File The `.gitignore` file is just a series of text patterns, one pattern per line. Blank lines and comments (line beginning with the `#` sign) are ignored. Let's now take a look at the most basic pattern possible: ``` foo ``` Though simple, this pattern packs quite a bit of hidden meaning. When intepreting ignore patterns, the first thing to look for is whether the pattern contains any forward slashes. Forward slashes change the meaning of a pattern drastically. This pattern has none but we will revisit the effect of slashes very shortly. So which files will our slashless `foo` pattern tell Git to ignore? First, it tells Git not to track any file or symlink with the name "foo" no matter which directory it's in. Second, Git ignores any file within any directory tree named "foo" anywhere in the repository. So this simple pattern ignores quite a bit! Here's some concrete examples of paths ignored by the `foo` pattern: | Paths Matched by `foo` | Match Explanation | |---------|-------------| | ./foo | Any path, either file or directory, matching the name "foo" in the root directory | | ./bar/foo | Any path matching "foo" in any subdirectory | | ./bar/foo/readme.md | Any file inside a directory named "foo" | | ./bar/foo/buzz/fuzz/blah.txt | Any file nested inside the directory tree named "foo" | It's helpful to reinforce two important points: * the `foo` pattern applies to both files and directories anywhere in our repository no matter how deeply nested * ignored directories cause any file anywhere inside of its directory tree to get ignored So we can see patterns without slashes are pretty greedy patterns and gobble up many paths. This isn't always desirable and we may want Git to be more selective about which paths patterns should match. Slashes can help us do that. ## Using Slashes to Make Patterns More Restrictive Slashes change the meaning of patterns significantly and help us limit which paths they match. The location of slashes–whether at the the beginning, middle or end of a pattern–is also significant. First, let's see how the pattern matches differently when adding a slash to the end of the humble `foo` pattern: ``` foo/ ``` By adding a slash to the end of the pattern, we tell Git to only match paths that are directories. In other words, Git will no longer ignore files named `foo`. However, it still ignores all files inside the directory tree of a directory named `foo`. It also applies to any directory named `foo` no matter where it is in the repository. But what if we only want to ignore the foo directory at the top level of our repository? We can turn again to the slash but this time we are going to add it to the beginning of the pattern: ``` /foo/ ``` Now only the single 'foo' directory in the root of the repository and all the files within it are ignored. What happens if we remove the trailing slash now and leave the leading slash? ``` /foo ``` Our pattern becomes slightly more permissive and now ignores a file named "foo" if it's in the root of the respository in addition to a "foo" directory. These pattern demonstrate: * Slashes at the end of a pattern only match directories (ignoring all files in its directory tree) * Slashes at the beginning make patterns relative to the root of the repository Finally, we can place slashes in the middle of our patterns. This has the same effect as a slash at the beginning of the pattern, making them relative to the root of the repository: ``` bar/foo/readme.txt ``` Now only the single readme.txt files in the foo directory in the bar directory in the root of the repository are ignored. Note that `/bar/foo/readme.txt` will behave in precisely the same way. If there are slashes in the middle of a pattern, we don't need one at the beginning. So as we can see, slashes can help tame our patterns and make them match fewer files. ## Using Path Wildcards in Patterns to Match More Paths While slashes are helpful, they can often be a little too restrictive. So Git gives us the double asterisk `**` wildcard to help us match many paths with a single pattern easily. Like with slashes, the meaning of the double asterisk changes depending on where we place them within a pattern. When at the beginning of a pattern, `**` matches all directories: ``` **/foo ``` This pattern matches all these example paths: ``` ./foo ./bar/boo/foo ./bar/buzz/fuzz/foo ``` Note that `**/foo` behaves precisely the same as as the simpler pattern `foo` without the double asterisks. When at the end of a pattern, `**` matches all the files and directories inside of a directory relative to the root of the repository: ``` foo/** ``` This matches all files inside the `foo` directory tree no matter how deeply nested. Note that it does not match something like `blah/foo` because the slash in the pattern forces it to be relative to the root of our repository. When the double asterisks are between slashes, they match zero or more directories: ``` foo/**/bar ``` This matches any path with 'bar' in it that is anywhere within a `foo` directory at the top level of the repository: * `foo/bar` matches * `foo/buzz/fuzz/bar` also matches * `blah/foo/bar` does not match You can use multiple double asterisks in a pattern: `**/foo/**/bar` Now the 'blah/foo/bar' path will be ignored. ## Character Wild Cards Git supplies us with another set of tools, often called "wildcards" but more accurate to say "shell globs," to make matching files easier. Let's say we want to ignore all log files which end with a 'log' file extension. It would be tedious to list the names of all these possible files. We can use the "*" wildcard character to help us. The '*' wildcard behaves like the asterisk character in a shell glob, matching zero or more of any character except the '/' character. The following pattern matches 'foo.log' and 'bar.log' but does not match 'foo/bar.log': ``` *.log ``` A subtle but important point: although the slash in 'foo/bar.log' path prevents a match, Git still ignores the 'bar.log' file. This is because *.log pattern matches the file name blah.log which gets ignored no matter what subdirectory it is in. By not matching slashes, the asterisk makes it possible to do something like: ``` foo/dir_*/*.log ``` This pattern ignores all log files in `/foo` in a directory startng with 'dir_'. Git also provides us with the '?' widlcard character which matches exactly one character except '/': ``` foo.log.? ``` This pattern matches 'foo.log.1' and 'foo.log.2' but not 'foo.log.12' or 'foo.log./'. Finally, the square brackets [] can be used to match any one of the characters inside a range: ``` foo.log.[a-z][0-9] ``` This pattern matches 'foo.log.a1' and 'foo.log.b2' but not 'foo.log.12'. You can also do something like: ``` foo.log.[axz] ``` This pattern matches 'foo.log.a', 'foo.log.x', 'foo.log.z' but not 'foo.log.c'. ## Negating Patterns Another useful feature of .gitignore files is the ability to negate patterns by preceding a patteern with "!" (exclamation point). This tells Git to stop ignoring files if they match the pattern. For example: ``` *.log !important.log ``` Here, Git ignores all log files not named 'important.log'. Negation allows you to pull off some interesting tricks. Consider this group of patterns in a .gitignore file: ``` * ! */ !*.gitignore !*.md ``` Together these patterns tell Git to ignore everything except for directories and .gitignore files and any file with an '.md' extension. The first line tells Git to ignore all files and all directories. The second line tells Git to stop ignoring all directories. The third and fourth lines tell Git to not ignore the types of files we want to track. ## Finer Points Now that you know the basic gitignore tools at your disposal, let's cover some subtler points of gitignore patterns that may not be immediately obvious. ### Ignoring a Directory is Different Than Ignoring the Files Inside It There are different approaches to ignoring files in a directory. We can match the directory with a pattern like `foo/` or `foo` . This tells Git to indirectly ignore everything in the directory tree of "foo" by ignoring the directory itself. Alternatively, we can use a wildcard to directly ignore all the contents of foo with `foo/*` but not the "foo" directory itself. This is a subtle but important distinction. The latter method provides us with more granular control over which files we ignore. For example, using the asterisk allows us to negate specific files in the directory: ``` foo/* !foo/README.txt ```