Great work!

XP to next level

BugEater

Untracked, Modified, Staged, Committed: A File's Four Moods

Learning Objectives

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

  • Name the four states a file can be in and what moves it between them
  • Explain what the staging area is and why Git has one at all
  • Predict which state a file is in from a git status output
  • Describe the three "trees" Git compares on every command

The Model That Makes Everything Else Obvious

Git keeps three copies of your project at all times. Learn these three and you have learned Git:

Tree Where it lives What it holds
Working Directory your visible folder the files as they are right now
Staging Area (index) inside .git the change you're assembling for the next commit
Repository (HEAD) inside .git the last committed snapshot, and every one before it

Everything Git tells you is a comparison between two of these three. That's the whole trick.

The Four States

A file moves through four states, and it moves in one direction:

untracked ──git add──▶ staged ──git commit──▶ committed
                          ▲                        │
                          │                     (edit it)
                          └───────git add───── modified

Untracked — the file exists in your folder, but Git has never recorded it. Brand-new files start here.

Modified — the file is in the repository, but the version on your disk differs from the last committed one. You edited it. Git noticed. Nothing has been recorded yet.

Staged — you've told Git "this exact change goes into the next commit." It's queued up, sitting in the staging area, waiting.

Committed — the change is written into the repository's history, permanently, with your name and message attached.

Notice that both untracked and modified files take the same road forward: git add puts them in the staging area, git commit writes them to history.

Why the Staging Area Exists

This is the step beginners want to skip, and the one that makes Git worth using.

Real work is messy. In one afternoon you might fix a typo in the regression checklist, rewrite half of an environment config, and drop in a screenshot for a bug you're still investigating. Three unrelated changes, sitting in one folder.

Without a staging area you'd have exactly one option: commit all of it, in one blob, under a message like "stuff". A year later, someone tries to understand when the config changed and finds it buried in a commit that also touched a checklist and a screenshot.

With a staging area, you choose:

git add checklist.md          # only the typo fix
git commit -m "Fix typo in login smoke checklist"

The config and the screenshot stay exactly where they were — modified and untracked, untouched, waiting for their own commits when they're ready.

The staging area is how you make history readable. It lets the commit reflect one idea instead of one afternoon.

Reading git status Like a Pro

git status groups files by state. Here's a repository with one of each:

On branch main

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   checklist.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
        modified:   config/test-env.yaml

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        screenshots/bug-441.png

Read the section headings, not the filenames:

  • "Changes to be committed" = staged. This is what the next commit will contain.
  • "Changes not staged for commit" = modified but not staged. Real edits, not going anywhere yet.
  • "Untracked files" = Git has never recorded these.

If a file appears under both the first and second heading, that's not a bug — it means you staged a change and then edited the file again. The staged version goes into the commit; the newer edit doesn't. Run git add again if you want both.

The whole model in one picture:

  WORKING DIRECTORY            STAGING AREA             REPOSITORY
  (files you edit)             (.git/index)             (.git — history)

  ┌────────────────────┐       ┌────────────────┐       ┌────────────────────┐
  │ checklist.md     M │  add  │                │ commit│ 4f2a1c9 Add cases  │
  │ test-env.yaml    M │──────▶│ checklist.md   │──────▶│ 91bd4e7 Update DB  │
  │ bug-441.png     ?? │       │                │       │ c7f0a12 Fix wait   │
  └────────────────────┘       └────────────────┘       └────────────────────┘

    M  = modified              staged: queued           permanent, with your
    ?? = untracked             for the commit           name and message

Only checklist.md was staged, so only checklist.md goes into the next commit. The config and the screenshot stay exactly where they are.

Pro Tip: Nothing in Git is permanent until it's committed — and even a commit is only permanent locally until you push. That means the staging area is a completely safe place to experiment. git add something you didn't mean to? git restore --staged <file> puts it back. No harm done.

Key Takeaways

  • Git maintains three trees: working directory, staging area, and repository
  • Files move untracked/modified → staged → committed, via git add and git commit
  • The staging area exists so a commit can contain one logical change instead of everything you did today
  • git status groups files by state — read the headings to know which state each file is in
  • A file can be staged and modified at the same time; only the staged version gets committed

Quiz

Which three trees does Git maintain?

A file has been committed before, and you have just edited it without running git add. What state is it in?

What is the staging area for?

git status shows the same file under both "Changes to be committed" and "Changes not staged for commit". Why?