Skip to content
academia.sh

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 .gitignore file; 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.

Start typing to search.

↑↓ Esc navigate · open · close