---
title: "Git's Four Areas"
description: "Move one change through the working tree, index, local repository, and remote."
source: https://vizlearn.app/cs/version-control/git-four-areas
---

# Git's Four Areas

Git's four areas feel mysterious when `add`, `commit`, and `push` are treated as synonyms for "save." They are three different copy operations across the working tree, index, local repository, and remote repository. Track which snapshot lives at each desk, and Git's common surprises stop being surprising.

## How Git's four areas work

Picture four desks in a row. The first holds the files you are editing. The second holds the exact pages selected for the next permanent record. The third stores records on your computer. The fourth stores records on another machine.

Git gives those desks specific names:

- The **working tree** is the checked-out set of files your editor reads and writes.
- The **index**, often called the staging area, is a proposed tree for the next commit.
- The **local repository** stores commit objects and local branch references such as `main`.
- The **remote repository** stores its own commit objects and branch refs such as `refs/heads/main`. This lesson labels that server-side branch `origin: main` to keep it distinct from local `main`.

The index deserves special attention. It is not a waiting room that becomes empty after a commit. It is a complete candidate snapshot. `git add app.py` copies the current content of `app.py` from the working tree into that candidate snapshot. A later edit changes the working file, but it does not silently rewrite the index.

A commit is also a snapshot, not a bag of changes. Git records the tree represented by the index, gives the new commit a parent and message, then moves the current branch reference to that commit. The working tree is not the source of the commit. That distinction is why a staged version and a newer unstaged version can coexist.

Finally, `git push` transfers commit objects that the remote lacks and asks the remote branch reference to advance. It does not upload the current working file or the current index directly.

## Git's four areas step by step

The default run uses one file and one branch so every boundary stays visible.

1. `edit app.py` writes `print("one")` into the working tree. The index, local repository, and remote repository are still empty.
2. `git add app.py` copies `print("one")` into the index. The working file remains in place; nothing has moved out of it.
3. A second edit changes the working file to `print("two")`. The index still holds `print("one")`. The two areas now contain different snapshots of the same path.
4. `git commit -m "save staged one"` creates C1 from the index, so C1 contains `print("one")`. Git then moves local `main` to C1. The newer working copy, `print("two")`, is still uncommitted.
5. `git push origin main` copies C1 to the remote repository and moves the remote's `main` branch to C1. Both repositories agree about the branch tip, while the working tree still differs from that committed snapshot.

The fourth step is the useful trap. If you expect the commit to contain `print("two")`, you are mentally skipping the index. Git does not ask, "What is in the editor now?" It asks, "What tree is staged?"

## What each boundary guarantees

The animation places a comparison badge between each pair of areas. Those badges are a compact way to read repository state.

Between the working tree and index:

- **same** means the tracked working content matches what is staged.
- **unstaged** means the working content differs from the staged candidate.
Between the index and local `HEAD`:

- **nothing staged** means committing would not produce a new tree.
- **staged** means the index differs from the tree stored by `HEAD`.

Between local `main` and the remote's `main`:

- **up to date** means both references point to the same commit.
- **ahead by N** means the local branch reaches N commits that the remote branch does not yet reach.

These comparisons describe snapshots and references, not a universal flow direction. Real Git can restore a file from the index, reset a reference, fetch remote objects, merge histories, and do much more. The left-to-right sequence shown here covers the everyday edit-add-commit-push path.

## Edge cases

The controls deliberately allow commands that do no work. Those outcomes are part of Git's model, not animation errors.

- Adding a missing `app.py` leaves the index unchanged.
- Adding a working file that already matches the index copies the same snapshot again, so no state changes.
- Committing when the index matches `HEAD` reports that nothing is staged. An unstaged working edit does not change that result.
- Pushing before any local commit has nothing to transfer.
- Pushing when local `main` and the remote's `main` already match is a successful no-op: everything is up to date.

Try the **Split snapshots** scenario, then commit immediately. Next, load **Dirty unstaged** and commit without adding. The first saves an older staged version; the second creates no commit at all. Those two runs isolate the job of the index.

## Common mistakes

**"`git add` tells Git which files changed."** Git already knows which working files differ. `add` writes selected content into the index. Re-run it after another edit if that newer content belongs in the next commit.

**"A commit saves everything in my project folder."** A commit saves the index. Untracked and unstaged content can remain outside it.

**"Committing clears the staging area."** After a commit, the index normally matches the new `HEAD`; it is not empty. A comparison says "nothing staged" because the two snapshots are equal.

**"Push sends my latest files."** Push negotiates and transfers commits, then updates a remote reference. A file that has not reached a local commit cannot reach the remote through push.

**"`origin/main` is the remote itself."** It is a local remote-tracking ref updated by fetch and related operations. The server owns a branch such as `refs/heads/main`; this lesson labels that server-side ref `origin: main` so you can compare the two tips without pretending they are the same ref.

## A Note on Simplification

This lesson uses one file, one linear branch, synthetic commit IDs such as C1, and an always-available remote. It leaves out the object database's blob/tree details, detached `HEAD`, merges, rebases, tags, upstream configuration, remote-tracking lag, authentication, hooks, packing, and network failures. The four-area distinction remains useful when those features return, but real Git commands may cross more than one boundary.
