Skip to content
Ameen Abozaid
PublishedBEG-046-L06

.gitignore

  • Lesson 6 of 8
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

Git tracks what you tell it to track, but git add . tells it to take everything. In a Java project "everything" includes things that should never be in history: compiled .class files, IDE settings folders, build output, and files that hold passwords. A .gitignore file is a short list of patterns that tells Git to leave such files alone.

Skipping this file causes two real problems. Generated files clutter every git status and every diff, and they cause pointless conflicts, since they change on every build. Worse, a password committed once stays in history. This lesson shows how to write a .gitignore, how to check that it works, what to do when a file was committed before you ignored it, and how to respond when a secret has already been committed.

You need the Git basics from lessons L02 to L05. Outputs come from Git 2.44; wording may differ on other versions.

Learning Objectives

By the end of this lesson you will be able to:

  • Explain what .gitignore does and what it cannot do.
  • Write patterns for build output, IDE folders, and secret files.
  • Use the pattern rules: comments, trailing /, *, and !.
  • Prove with git status and git check-ignore -v that a file is ignored.
  • Stop tracking a file that was committed earlier, using git rm --cached.
  • Describe the correct response to a password committed by mistake.

Written Explanation

What goes in the file

Create a text file named exactly .gitignore (with the leading dot, no extension) in the top folder of the repository. Each line is a pattern for files Git should not track. Here is a typical start for a Java project:

text
# build output
target/
out/
*.class

# IDE
.idea/
*.iml

# secrets
.env

What each group is for:

PatternWhy ignore it
*.classCompiled bytecode. It is rebuilt from your .java files by the compiler
target/Output folder of the Maven build tool (taught later, in BEG-054)
out/The folder IntelliJ IDEA often uses for compiled output; check your own IDE's setting
.idea/, *.imlIntelliJ IDEA settings that differ per machine
.envA common name for a file holding local secrets such as passwords

You do not have to use Maven or IntelliJ yet. The list is safe to start with, and you add lines as the project grows. Other IDEs use other folder names, such as .vscode/ or bin/; look at what git status shows as untracked and decide.

Pattern rules you need

  • A line starting with # is a comment; blank lines are ignored.
  • * matches any text in a name: *.class matches every file ending in .class, in any folder.
  • A trailing / means "directories only": target/ matches a folder named target and everything under it, but not a plain file with that name.
  • A leading ! negates a pattern and re-includes a file that an earlier line excluded. (If a parent folder is ignored, you cannot re-include a file inside it.)
  • A leading / anchors a pattern to the folder that contains the .gitignore: /notes.txt matches only the top-level one.
  • ** matches nested folders, for example **/logs matches a logs folder at any depth.
  • The last matching pattern wins, so put an ! exception after the broader pattern it relaxes. For instance, *.class followed by !Keep.class ignores all compiled files except Keep.class.

Commit the .gitignore itself. It belongs to the project, and teammates then get the same rules.

Proving it works

Before the file exists, git status -s lists everything:

text
?? .env
?? .idea/
?? A.class
?? A.java
?? target/

After creating .gitignore with the lines above:

text
?? .gitignore
?? A.java

Ignored files disappear from the list. To ask why a specific file is ignored, use:

bash
git check-ignore -v .env target/app.jar A.class
text
.gitignore:11:.env	.env
.gitignore:2:target/	target/app.jar
.gitignore:4:*.class	A.class

Each line says which file, which line number, and which pattern matched. If a file you expect to be ignored is not listed, no pattern matches it. After git add . and a commit, git ls-files lists the tracked files: only .gitignore and A.java. That is the "proof" that target/ stays untracked.

The trap: ignoring does not untrack

.gitignore only affects files Git is not already tracking. If a file was committed earlier, Git keeps tracking it and keeps showing its modifications, even if a pattern now matches. This is the most common .gitignore problem. The fix is to remove the file from Git's tracking while keeping it on disk:

bash
git rm -r --cached Account.class Main.class
git status -s
text
D  Account.class
D  Main.class

--cached removes the file only from the staging area and the next commit, not from your folder. Then commit:

bash
git commit -m "Stop tracking compiled class files"

From now on the .gitignore pattern takes over. Note that the old commits still contain the files; only future commits stop tracking them.

Secrets: ignoring is not a cure

Putting .env in .gitignore before the first commit keeps it out of history. But if a password was already committed, ignoring it later does not remove it: it is still in the old commits, and anyone with a copy of the repository can read it. Rewriting history can hide it from new clones, but it does not reach copies others already made.

The safe rule is:

A secret that has been committed once must be treated as leaked: change the password or revoke the key first.

GitHub's guidance on removing sensitive data says the same: rotate the credential first, because rewriting history alone does not erase copies in other clones and forks. In practice, keep real secrets out of your project folder (or in an ignored file) and commit only a template such as .env.example containing fake values, so teammates know which settings are needed.

Examples

Example 1: Ignoring compiled files (simple)

You compile a Java file and git status -s shows:

text
?? A.class
?? A.java

Create .gitignore containing *.class. Now git status -s shows ?? .gitignore and ?? A.java. Stage and commit them:

bash
git add .gitignore A.java
git commit -m "Add A class and ignore compiled files"

Example 2: Fixing a file that was already committed (practical)

Last week you ran git add . in a hurry and committed Account.class and Main.class. Today you add *.class to .gitignore, but git status still shows M Account.class after every compile. Git is still tracking it. Fix:

bash
git rm -r --cached Account.class Main.class
git commit -m "Stop tracking compiled class files"

After this, compiling no longer changes git status, and git check-ignore -v Main.class reports the matching .gitignore line.

Example 3: A password in a settings file (realistic)

A project reads its database password from .env. You create .env with DB_PASSWORD=secret (a made-up value here), and git status -s shows ?? .env. You add .env to .gitignore before running git add ., so it is never staged. The file stays on your disk and out of the repository. For teammates, you commit .env.example with DB_PASSWORD=change-me.

Now the bad version of the story: you committed .env last week and notice today. You add it to .gitignore and run git rm --cached .env. The future is safe, but the password is still in the old commit. The correct response is to change the password, and treat the old one as public.

Backend Insight

Backend projects handle exactly the values that must not leak: database passwords, API keys, tokens. Real incidents begin with a secret in a repository, especially a public one. Later modules show better ways of supplying settings, such as environment variables (you used them in BEG-006), so that secrets never live in the project folder at all.

Build output matters too. A Maven project can produce many files in target/; committing them bloats the repository, causes conflicts, and can hide real changes. A team's first commit should include a .gitignore, and in L07 you will do that for Bank Accounts. Online template collections of .gitignore files for Java projects exist and are a reasonable starting point, but read each line you copy.

Common Mistakes

Mistake 1: Committing target/ or .idea/

  • Incorrect: git add . in a project with no .gitignore.
  • Correct: create .gitignore first, run git status, and only then add.

Mistake 2: Expecting .gitignore to untrack a committed file

  • Incorrect: "I added *.class but Git still tracks Main.class."
  • Correct: run git rm --cached Main.class, commit, and the pattern then applies.

Mistake 3: Treating an ignored secret as safe after it was committed

Once in history, a secret is exposed. Change it. Hiding it from future commits is not enough.

Mistake 4: Forgetting the trailing slash or the dot

target (no slash) matches files and folders with that name; gitignore without the leading dot is just another file that Git does not read. The file must be named .gitignore.

Exercises

Level A: Foundation

A1. What does the pattern *.class match?

Show solution A1

A1. Every file whose name ends in .class, in any folder below the .gitignore.

A2. What is the difference between target/ and target?

Show solution A2

A2. target/ matches only directories named target (and everything inside). target matches both files and directories with that name.

A3. Should .gitignore itself be committed? Why?

Show solution A3

A3. Yes. It is part of the project and shares the rules with everyone who clones it.

Level B: Application

B1. Write a .gitignore for a Java project that excludes compiled files, IntelliJ settings, an output folder, and a local secrets file.

Show solution B1

B1.

text
# compiled and build output
*.class
out/
target/

# IDE
.idea/
*.iml

# secrets
.env

B2. In a new repository, create Test.java, compile it, and run git status -s. Add a .gitignore and run git status -s again. What changed?

Show solution B2

B2. Before: ?? Test.class and ?? Test.java (and so on). After adding a .gitignore containing *.class: Test.class is gone from the list; .gitignore and Test.java remain.

B3. Which command tells you why a particular file is ignored? Write its output shape.

Show solution B3

B3. git check-ignore -v <file>. The output has the form .gitignore:LINE:PATTERN<tab>FILE.

Level C: Challenge

C1. You committed Main.class yesterday and added *.class today, yet git status still lists it as modified after compiling. Give the exact commands to fix it, and say what remains in the old commits.

Show solution C1

C1.

bash
git rm --cached Main.class
git commit -m "Stop tracking compiled class files"

The old commits still contain Main.class; only later commits stop tracking it.

C2. A teammate pushes a commit containing DB_PASSWORD=hunter2 and says, "I'll add .env to .gitignore now, so it's fine." Explain what is wrong and list the steps they should take.

Show solution C2

C2. Ignoring does not remove what is already committed or pushed. The password must be treated as leaked: change it at once (for example, on the database), then add .env to .gitignore, run git rm --cached .env, commit, and tell whoever needs to know. Cleaning history is a separate, optional step and does not protect copies others already have.

Mini Challenge

Create a small repository with a src folder, a compiled class, an out folder, a fake .env file with a made-up value, and an IDE folder named .idea. Before any git add, write the .gitignore. Prove with git status -s and git check-ignore -v that every unwanted item is ignored. Then add a second fake file, config.local, commit it by mistake, and fix it with git rm --cached and a new ignore line.

Check your work: git ls-files lists only the files you want; each ignored item is explained by git check-ignore -v; and you can state what remains in the older commit.

Summary & Next Lesson

Key takeaways

  • .gitignore lists patterns of files Git must not track; commit it.
  • Ignore build output (target/, out/, *.class), IDE folders (.idea/), and secrets (.env).
  • git status -s and git check-ignore -v prove a pattern works.
  • Ignoring does not untrack a committed file: use git rm --cached.
  • A committed secret is exposed: change it.

Next lesson

Next is "Demo: putting Bank Accounts under Git": you will apply lessons L02 to L06 to a real project, including a planned mistake with git add ..

Self-check

  1. Which file lists the patterns Git should ignore?
  2. Should target/ be committed? Why?
  3. You added a pattern, but Git still tracks a file. What do you run?
  4. What does the trailing / in a pattern mean?
  5. A password was committed. What is the best response?
Show answers
  1. .gitignore.
  2. No. It is generated output that can be rebuilt from source.
  3. git rm --cached <file>, then commit.
  4. The pattern matches only directories.
  5. Change the password (treat it as leaked); ignoring the file afterwards is not enough.

References

  • Git documentation, gitignore. It supports the pattern rules (comments, trailing / for directories only, ! negation, last match wins, no re-including inside an ignored folder) and that already-tracked files are not affected unless removed with git rm --cached.
  • Pro Git book, "Recording Changes to the Repository". It supports git rm --cached keeping the file on disk and the .gitignore pattern examples.
  • GitHub Docs, "Removing sensitive data from a repository". It supports rotating a leaked credential first and that rewriting history does not remove copies in clones and forks.
  • Not verified from an official page: that IntelliJ uses out/ by default (hedged in the text), and that Maven writes to target/ (taught in BEG-054). Command outputs come from running Git 2.44.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.