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.