Lesson 09 / 15
Ignore Rules
Files that should not enter the repository; `.gitignore` pattern syntax; finding a rule's source, and why ignore rules do not apply to tracked files.
Contents
So far, the repository holds only files written by hand. But files also get
produced while working: the script’s output, backups left by the editor, build
results. These get listed at every git status call, lowering the visibility of
the real changes, and can end up in a commit by mistake.
This lesson takes up which files are kept out of the repository, and how this decision gets recorded.
What Should Not Enter the Repository
Four classes are distinguished:
- Derived files. Everything that can be produced from the sources already in the repository: build outputs, data produced by scripts, temporary reports. Keeping them in history is unnecessary, because they can be regenerated from the source; what is more, they show up as a constant diff since they change on every regeneration.
- Local environment files. Editor settings, metadata files the operating system leaves in directories, personal notes. These belong to the person, not the project.
- Secrets. Passwords, access keys, private certificates. Keeping these out of the repository is not a matter of tidiness, it is a security requirement.
- Large binary files. Every version is stored as a separate object and cannot be compressed, since no diff can be taken. They take up space in every copy of the repository.
The third item calls for a separate warning. As noted in the previous topic, in a distributed repository, history spreads to every copy. Even if a password is deleted after being committed, the deletion only adds a new commit; the old commit containing the password stays in place and is present in every copy of the repository. In this case, the only correct action is to invalidate the secret.
The .gitignore File
In the example repository, running the script produces a log file and an output directory; the editor also leaves a backup:
git status --short
?? README.md~ ?? ara.log ?? cikti/
The rules are written into a text file named .gitignore:
# Betiğin ürettiği çıktılar ara.log cikti/ # Düzenleyici yedekleri *~
git status --short
?? .gitignore
Three entries dropped off the list; what is left is the file carrying the rules
itself. .gitignore is committed to the repository: the rules belong to the
project and should be the same in every copy of the repository.
git add .gitignore git commit -m "Üretilen dosyaları yoksayma listesine al"
[main 9b23cc3] Üretilen dosyaları yoksayma listesine al 1 file changed, 6 insertions(+) create mode 100644 .gitignore
Pattern Syntax
Every line is a pattern: a string matched against path names that can contain wildcard characters.
| Pattern | Meaning |
|---|---|
*.log |
Files with the .log extension, at any depth |
cikti/ |
Only directories named cikti, and their contents |
/gecici.tmp |
Only gecici.tmp at the repository root |
not?.txt |
Single-character wildcard: not1.txt, not2.txt |
a/**/veri.bin |
veri.bin at any depth under a |
!ara.log |
Excludes a file covered by a previous rule from the rule |
# text |
A comment line |
Two details are often confused. The first is the position of the slash: if
there is a slash inside or at the start of the pattern, the pattern is resolved
relative to the directory the .gitignore file is in; otherwise it matches at
any depth. /gecici.tmp catches only the file at the root, not
alt/gecici.tmp. The second is the trailing slash: cikti/ matches only
directories, not a file named cikti.
Rules are evaluated in order, and the last matching rule takes effect. This
is why a rule starting with ! has to be written after the pattern it covers;
if written first, it has no effect.
Why a File Is Ignored
In long rule lists, which line is in effect can be asked directly:
git check-ignore -v ara.log cikti/son-arama.txt "README.md~"
.gitignore:2:ara.log ara.log .gitignore:3:cikti/ cikti/son-arama.txt .gitignore:6:*~ README.md~
Every line gives the file the rule is in, the line number, the pattern, and the
matching path. If a rule starting with ! matches, it is listed too; in that
case, the file is not ignored, because the last matching rule excluded it from
the rule.
All ignored files can also be listed:
git status --short --ignored
?? .gitignore !! README.md~ !! ara.log !! cikti/
The !! mark denotes ignored entries.
Ignoring Does Not Apply to Tracked Files
A file holding local notes gets committed by mistake:
git commit -m "Yerel notları ekle"
[main 7783378] Yerel notları ekle 1 file changed, 3 insertions(+) create mode 100644 notlar-yerel.md
The mistake is noticed, and the file name is added to .gitignore. But nothing
changes:
git ls-files
.gitignore README.md ara.sh notlar-yerel.md terimler.txt
The file is still tracked. The rule not applying is not a flaw, it is a consequence of the definition: ignore rules determine whether untracked files get listed or not. If a file has a record in the staging area, it is already tracked, and the rules have nothing to do with it. Otherwise, adding a rule could make tracked files silently drop out of history.
For the same reason, git check-ignore stays silent too: by default, the
command does not evaluate tracked paths.
The fix is to remove the file from the staging area:
git rm --cached notlar-yerel.md
rm 'notlar-yerel.md'
The --cached option deletes only the record in the staging area; the file in
the working directory stays in place. Without the option, the file is deleted
from disk too.
git commit -a -m "Yerel notları izlemeden çıkar"
[main cb8f7ee] Yerel notları izlemeden çıkar 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 notlar-yerel.md
After this commit, the file is no longer tracked, and the .gitignore rule
takes effect. The file’s old content, however, still sits in history: commit
7783378 is still reachable. Ignoring does not clean up the past; it only puts
the future in order.
Three Ignore Layers
| Location | Scope | Shared? |
|---|---|---|
.gitignore |
The directory it is in, and below | Yes, committed to the repository |
.git/info/exclude |
This repository only | No, stays local |
The file pointed to by core.excludesFile |
All of the user’s repositories | No |
The distinction is made by this criterion: does the rule belong to the
project, or to a work habit? The build output’s name belongs to the
project and is written to .gitignore. The temporary files left by the editor
in use belong to the person; rather than adding these to every project’s
.gitignore, the right place to write them is the user-level file.
Rules specific to this repository but not meant to be shared are written to the
.git/info/exclude file, and it uses the same syntax:
git check-ignore -v deneme-1.txt
.git/info/exclude:7:deneme-*.txt deneme-1.txt
Since the output’s first field shows which file the rule came from, confusion between the three layers is resolved with the same command.
Summary
- Derived files, local environment files, secrets, and large binary files should not enter the repository.
- Rules are written to the
.gitignorefile; the file is committed to the repository and applies in every copy. - In patterns, the slash’s position determines the scope; a trailing slash limits the pattern to directories; the last matching rule takes effect.
- Ignore rules affect only untracked files; a tracked file has to be removed
from tracking with
git rm --cached. - Rules can be kept in three layers: the file committed to the repository, the repository-specific local file, and the user-level file.
Next Step
The basic cycle is complete: a change is made, staged, committed, history is read. But this cycle has been told in one direction only. What happens when the wrong file gets staged, an edit needs to be taken back, or a commit is written wrong? The next topic takes up the tools for undoing, and its first lesson starts with the narrowest-scoped ones — unstaging and restoring a file.
To keep your progress and take notes, Log in
My notes
Log in to take notes.