Excluding from version control

Author

Marie-Hélène Burle

Not everything should be under version control, yet we don’t want a cluttered working directory. The solution: a list of files or patterns that Git disregards.

What to exclude

There are files you really want to put under version control, but there are files you shouldn’t.

Put under version control:

  • Scripts
  • Manuscripts and notes
  • Makefile & similar

Do NOT put under version control:

  • Non-text files (e.g. images, office documents)
  • Your initial data
  • Outputs that can be recreated by running code (e.g. graphs, results)

However, you don’t want to have such documents constantly showing up when you run git status. In order to have a clean working directory while keeping them out of version control, you can create a file called .gitignore and add to it a list of files or patterns that you want Git to disregard.

Your turn:

In the case of our mock project,

  • what should we put under version control?
  • what should we ignore?

How to exclude

The .gitignore file

To exclude files from version control, create a file called .gitignore in the root of your project and add those files to it, one per line.

Example:

# Create .gitignore and add 'graph.png' to it
echo graph.png > .gitignore

# `>` would overwrite the content. `>>` appends
echo output.txt >> .gitignore

You can also ignore entire directories.

Example:

echo /results/ >> .gitignore

Finally, you can use globbing patterns to ignore all files matching a certain pattern.

Example:

# Exclude all .png files
echo *.png >> .gitignore
.gitignore syntax

Each line in a .gitignore file specifies a pattern.

Blank lines are ignored and can serve as separators for readability.

Lines starting with # are comments.

To add patterns starting with a special character (e.g. #, !), that character needs to be escaped with \.

Trailing spaces are ignored unless they are escaped with \.

! negates patterns.

Patterns ending with / match directories. Otherwise patterns match both files and directories.

/ at the beginning or within a search pattern indicates that the pattern is relative to the directory level of the .gitignore file (usually the root of the project). Otherwise the pattern matches anywhere below the .gitignore level.

Examples:

/foo/bar/ matches the directory foo/bar, but not the directory a/foo/bar
foo/bar/ matches both the directories foo/bar and a/foo/bar

* matches anything except /.

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

A leading **/ matches all directories.

Example:

**/foo matches file or directory foo anywhere. This is the same as foo.

A trailing /** matches everything inside what it precedes.

Example:

abc/** matches all files (recursively) inside directory abc

/**/ matches zero or more directories.

Example:

a/**/b matches a/b, a/x/b, and a/x/y/b

Your turn:

Create a .gitignore file suitable for our mock project.

The .gitignore is a file like any other file, so you’ll want to stage and commit it:

git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    .gitignore
    src/

nothing added to commit but untracked files present (use "git add" to track)

Notice how data/ is not listed in the untracked files anymore.

We stage our .gitignore file:

git add .gitignore
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   .gitignore

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    src/

And we create a new commit:

git commit -m "Add .gitignore file with data and results"
git status
[main a1df8e5] Add .gitignore file with data and results
 1 file changed, 2 insertions(+)
 create mode 100644 .gitignore
git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    src/

nothing added to commit but untracked files present (use "git add" to track)

Let’s create a third commit with the Python script:

git add src/script.py
git commit -m "Add first draft of script"
[main ca3c036] Add first draft of script
 1 file changed, 7 insertions(+)
 create mode 100644 src/script.py
git status
On branch main
nothing to commit, working tree clean

What does “working tree clean” mean? In the next section, we will talk about the three file trees of Git.