Skip to main content
RunBook Academy

Git, CI/CD & GitOpsII · Git ArchitectureArchitecture

The working tree, the index, and the repository — the three areas Git operates on

Foundation⏱ ~19 mingit

What you'll learn

  • Name the three areas Git operates on and what each one represents
  • Explain how git status maps filesystem state to the three areas
  • Trace the path of a file from the working tree through the index into the repository
  • Choose the correct git diff form to compare the area pair you care about
  • Recognise why the index is the staging area and not the working tree

Prerequisites

None — start here.

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Every Git command you will ever run is a movement of bytes between three areas: the working tree, the index, and the repository. The working tree is the files you can see and edit; the index is the candidate snapshot that will become the next commit; the repository is the append-only object store that holds every commit, tree, and blob ever written. The first lesson of Git Architecture is to see these three areas as separate, and to recognise which command moves state between which pair.

The three areas

flowchart LR
    WT["Working tree\n(filesystem)"] -- "git add" --> IDX["Index\n(.git/index)"]
    IDX -- "git commit" --> REPO["Repository\n(.git/objects)"]
    REPO -- "git checkout" --> WT
    REPO -- "git reset" --> IDX
  • Working tree. The directory you ran git init (or cloned) into. It contains the files you edit, the files you have not yet staged, and the files you have not yet committed. It is the only area a human edits directly.
  • Index. A single binary file at .git/index that holds the list of paths and their blob object IDs that will form the next commit. The index is not a directory; it is a serialised manifest. When you run git add, Git writes the corresponding blob into the object store and updates the index entry to point at that blob.
  • Repository. The .git/objects directory and the refs under .git/refs. It contains every commit, every tree, every blob, and every tag. It is append-only: nothing is ever rewritten in place; new state is a new object whose hash is determined by its contents.

How git status reads the three areas

git status is the cheapest way to see the three areas at once. It compares the working tree against the index, and the index against HEAD, and prints the differences:

git status
# On branch main
# Changes to be committed:
#   modified:   terraform/main.tf
# Changes not staged for commit:
#   modified:   ansible/hosts.yml
# Untracked files:
#   playbooks/rotate-creds.yml

Each line names a state transition:

  • Changes to be committed — the index differs from HEAD. The file is staged but not yet committed.
  • Changes not staged for commit — the working tree differs from the index. The file has been edited since the last git add.
  • Untracked files — the working tree contains a path that is not in the index at all.

Reading git status correctly is the difference between a correctly-staged commit and a commit that ships half a Terraform plan.

How a commit moves state through the three areas

The lifecycle of a single change is a deterministic path:

# 1. Edit the working tree
echo 'resource "aws_s3_bucket" "logs" {}' >> terraform/main.tf

# 2. Inspect the working tree vs the index
git diff
# (shows the new line)

# 3. Stage the change: working tree -> index
git add terraform/main.tf

# 4. Inspect the index vs HEAD
git diff --cached
# (shows the same new line, now from the index)

# 5. Commit the index: index -> repository
git commit -m "feat(terraform): add S3 logs bucket"
sequenceDiagram
    participant Dev as Engineer
    participant WT as Working tree
    participant IDX as Index
    participant REPO as Repository

    Dev->>WT: edit terraform/main.tf
    Dev->>WT: git status (working tree vs index)
    Dev->>IDX: git add terraform/main.tf
    Dev->>IDX: git diff --cached (index vs HEAD)
    Dev->>REPO: git commit
    REPO->>REPO: store tree, commit, update ref

The single most important step is step 5: git commit reads the index, builds a tree object from it, writes a commit object pointing at that tree, and updates the current branch ref. The bytes in the working tree are not what gets committed — the index is.

Diff forms map to area pairs

git diff with no arguments compares the working tree to the index. Every other form names a specific pair:

git diff              # working tree vs index
git diff --cached     # index vs HEAD
git diff HEAD         # working tree vs HEAD (the "is my working tree clean?" check)
git diff $COMMIT     # working tree vs $COMMIT

A common production mistake is using git diff to ask “what will my next commit contain?” The answer is git diff --cached, because the index is what gets committed, not the working tree.

Production discipline

  1. Trust git status, not your editor. Your editor may show files as “saved” while the working tree still differs from the index. Always read git status before committing.
  2. Stage deliberately. git add -A stages everything. In an infrastructure repository, a stray debug print or a local environment override can sneak into a commit. Stage per feature: git add terraform/main.tf is better than git add . for a commit that should describe one logical change.
  3. Read the index before the commit. A 30-second git diff --cached before git commit catches the wrong file in the right commit. In an infrastructure repository, the cost of a wrong commit is a wrong apply.

Cross-course references

  • Linux for Production Sysadmins - Part IX (Filesystem) covers the inode-level representation of a file; the index is the Git analogue of a directory entry, listing paths and the OIDs of their current blobs.
  • Ansible for Production Sysadmins - Part XXXVII (RepoArch) treats the index as the boundary between untrusted working changes and the repository that the playbook will be run from.
  • Terraform for Production Sysadmins - Part IX (State) covers the equivalent of the index for Terraform: the plan file, which is the candidate next state reviewed before apply.

Quiz

Knowledge check · 4 questions

  1. Q1. An engineer has edited a Terraform file and run `git add terraform/main.tf`. They then run `git diff` and see nothing, but `git diff --cached` shows the full change. What does this tell them about the three areas?

  2. Q2. The index is held in memory and is discarded when a Git process exits.

  3. Q3. A CI job wants to attach the exact Terraform plan that will be applied to the commit message. Which area must the plan be derived from, and why?

  4. Q4. Diagnose why a commit contains a change that the engineer did not intend to ship.

    An engineer edits `terraform/main.tf` to add a new S3 bucket, then runs `git add terraform/main.tf` and `git commit -m 'add logs'`. The CI plan shows an unrelated change to `terraform/backend.tf` that the engineer did not edit. The engineer insists they only edited `main.tf` and only staged `main.tf`.

Passing score: 75%. Answers are checked in this browser.