GitWorking Directory, Staging, Repository

Working Directory, Staging, Repository

Git has a unique design that confuses almost everyone at first: every file you work on exists in three places at once. Once you internalise these three places, almost every Git command becomes obvious — they are all about moving content between them.

The three trees

The mental picture

Text
┌──────────────┐    git add     ┌──────────────┐   git commit  ┌──────────────┐
│   Working    │ ─────────────▶ │   Staging    │ ────────────▶ │ Repository   │
│  Directory   │                │  (index)     │               │   (.git)     │
│              │ ◀───────────── │              │ ◀──────────── │              │
└──────────────┘  git restore   └──────────────┘ git restore   └──────────────┘
                                                   --staged

  your editor /    .git/index           .git/objects + refs
  filesystem       (binary file)         (objects database)
1. The Working Directory

The working directory (also called the working tree) is what you actually see and edit. It is just the files in the folder outside the .git/ directory. Open your editor and you are looking at the working directory.

  • You can change anything here freely — Git tracks but does not interfere.

  • A file in the working directory might be tracked, untracked, modified, or unchanged.

  • Changes here are invisible to Git history until you stage and commit them.

2. The Staging Area (Index)

The staging area is a holding pen for the next commit. Think of it as a draft of the commit you are about to make.

  • Implemented as the file .git/index (binary).

  • Updated by git add — staging tells Git: “include this version of this file in my next commit.”

  • Lets you commit some changes but not others — even within the same file (via git add -p).

  • After a commit, the staging area still holds the just-committed content (now matching the repo).

Why a staging area?
Other VCS tools commit *everything that has changed* whenever you commit. Git separates “I have edited this” from “I want to record this in history,” which lets you craft cleaner, smaller commits.
3. The Repository

The repository is the permanent, content-addressed history — every commit, every file, every branch — stored inside the .git/ folder. Once content reaches the repository it is immutable (and reachable through the commit graph).

  • Updated by git commit — moves staged content into a new commit.

  • Holds all past states of the project, not just the latest.

  • Survives even if you delete or replace your working directory — git restore . rebuilds it.

Watch the three trees move

A full lifecycle in one terminal session

Bash
mkdir trees-demo && cd trees-demo
git init

# Step 1: create a file. Only exists in the working directory.
echo "hello" > greeting.txt
git status
#   Untracked files: greeting.txt

# Step 2: stage it. Now in both working dir AND index.
git add greeting.txt
git status
#   Changes to be committed: new file: greeting.txt

# Step 3: commit it. Now in all three trees.
git commit -m "Add greeting"
git status
#   nothing to commit, working tree clean

# Step 4: modify the file. Working dir != index now.
echo "world" >> greeting.txt
git status
#   modified: greeting.txt

git diff               # diff: working dir vs index
# +world

# Step 5: stage the change
git add greeting.txt
git diff               # nothing — wd matches index
git diff --staged      # diff: index vs repo
# +world

# Step 6: commit
git commit -m "Add world"
git log --oneline
# 2nd commit: Add world
# 1st commit: Add greeting
Moving things backward

Restoring files

Bash
# Discard changes in the working directory (back to the index state)
git restore greeting.txt

# Unstage a file (back to the working directory)
git restore --staged greeting.txt

# Both: throw away local edits AND unstage
git restore --source=HEAD --staged --worktree greeting.txt
File states

Each file is in one of these states based on where it differs:

  • Untracked — exists in the working directory; Git has never seen it.

  • Tracked, unmodified — present in all three trees and identical.

  • Tracked, modified — working dir differs from index. git status says “modified.”

  • Staged — working dir matches index, but index differs from the last commit. git status says “Changes to be committed.”

  • Staged & modified — you staged a version, then made more changes. The file is in all three states at once.

git status reads all three trees

Anatomy of git status

Bash
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:               ← differences: staging vs repo
  (use "git restore --staged <file>..." to unstage)
        new file:   index.html

Changes not staged for commit:         ← differences: working dir vs staging
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   index.html

Untracked files:                       ← in working dir, not in staging
  (use "git add <file>..." to include in what will be committed)
        notes.txt

That output is just git status walking each tree and reporting the differences. Once you can read the output in terms of the three trees, you understand git status completely.

A useful question

When something confuses you in Git, ask yourself: “which tree is the content in, and which tree do I want it in?” The answer almost always names the command you need:

  • Working dir → staging: git add

  • Staging → repository: git commit

  • Repository → staging: git restore --staged --source=HEAD

  • Staging → working dir: git restore

  • Repository → working dir AND staging: git restore --source=HEAD --worktree --staged

  • Repository → branch pointer: git reset

Tip
Beginners who skip this section spend months memorising recipes. The three-trees model is the single most useful concept in Git. Re-read this page after a week of practice — it will click even harder.