Git, CI/CD & GitOpsII · Git ArchitectureArchitecture
The working tree, the index, and the repository — the three areas Git operates on
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
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/indexthat 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 rungit add, Git writes the corresponding blob into the object store and updates the index entry to point at that blob. - Repository. The
.git/objectsdirectory 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
- Trust
git status, not your editor. Your editor may show files as “saved” while the working tree still differs from the index. Always readgit statusbefore committing. - Stage deliberately.
git add -Astages 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.tfis better thangit add .for a commit that should describe one logical change. - Read the index before the commit. A 30-second
git diff --cachedbeforegit commitcatches 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
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?
Q2. The index is held in memory and is discarded when a Git process exits.
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?
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.