Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Git & GitHub›Git Architecture — Working Tree, Index, Repository

Git Fundamentals

Git Architecture — Working Tree, Index, Repository

Git architecture consists of three distinct layers: the Working Tree, the Index, and the Repository, which facilitate a controlled workflow for versioning. Understanding this separation is essential because it allows developers to curate specific changes before committing, ensuring that only intentional modifications are recorded in the history. You reach for this architectural knowledge whenever you need to debug a broken commit, recover lost work, or selectively stage complex code changes.

The Working Tree: Your Sandbox

The Working Tree is the actual directory on your filesystem where you edit files, run build scripts, and interact with your project's source code directly. It represents the state of your project at the current moment, regardless of what has been saved or committed to Git. Think of this as your active workbench where files are malleable and constantly changing as you develop features. Because the Working Tree is independent of the internal Git storage mechanisms, any file modification you make here remains entirely invisible to Git until you explicitly inform it of your intent to track those changes. This separation provides a safe environment where you can experiment, break things, and explore solutions without polluting your project's formal history. The Working Tree allows you to maintain multiple experimental versions of a file simultaneously without forcing you to commit half-finished or broken work to your repository, keeping the project's long-term integrity shielded from the chaotic nature of active development cycles.

# View the current status of your working tree
git status

# Create a new file in your working tree
echo "print('Hello World')" > app.py

# The file exists in the working tree but is 'untracked' by Git
ls -l app.py

The Index: The Staging Area

The Index, often referred to as the Staging Area, acts as a sophisticated buffer between your Working Tree and the Repository. Its primary purpose is to allow you to prepare a logical snapshot of your changes before they are permanently etched into the Git history. When you run a command to add a file, you are essentially telling Git to calculate the hash of that file's content and register that state in the Index. This is critical because it decouples the act of saving a file from the act of committing it. By utilizing the Index, you can meticulously curate what goes into your next commit, allowing you to exclude debug statements, temporary files, or unrelated refactors. This architecture prevents 'dirty' commits and ensures that every entry in your project's log represents a cohesive and meaningful change. Without the Index, every single change in your Working Tree would be forced into a commit, which would lead to an unmanageable and messy project history that is impossible to audit effectively.

# Add a specific file to the Index (Staging Area)
git add app.py

# Check the status to see the file moved to the 'Changes to be committed' section
git status

# Remove a file from the Index without deleting it from the Working Tree
git reset app.py

The Repository: The Immutable History

The Repository is the heart of Git, located within the hidden .git directory, and it contains all the snapshots, branch pointers, and configuration data required to reconstruct the project history. When you perform a commit, Git takes the current state of the Index—which reflects the precise arrangement of files you prepared earlier—and archives it as a immutable object. This storage mechanism relies on directed acyclic graphs to link these snapshots, providing a permanent and verifiable audit trail of how the project has evolved over time. Because the Repository stores data as persistent snapshots rather than delta-based file changes, Git can perform extremely fast branch switching and history traversal. Understanding that the Repository only knows what has been formally committed from the Index is vital; it explains why your local file edits never reflect in the project history until the staging and commitment cycle is complete. This architecture ensures that once a commit is created, it acts as a reliable checkpoint that you can always return to, effectively safeguarding your code against accidental deletions or regression errors.

# Create a permanent record of the staged changes in the Repository
git commit -m "Initialize the application structure"

# View the internal history to confirm the commit was saved
git log --oneline

# Check the contents of the internal Git objects folder
ls -F .git/objects/

Moving Data Between Layers

Data flows through these layers in a strictly defined sequence: from the Working Tree to the Index via staging, and from the Index to the Repository via committing. This architectural flow is designed to be unidirectional, ensuring that you always have a conscious opportunity to verify your work before it enters the historical record. When you modify a file in your Working Tree, it is simply a byte stream on your disk. Adding that file to the Index transforms that data into a Git-managed object that is ready to be bundled into a snapshot. Committing then freezes that snapshot into the Repository. This architecture is powerful because it allows for granular operations like partial staging, where you only include specific lines or blocks of a file in a commit. By mastering the movement of data between these stages, you gain full control over the narrative of your project, allowing you to rewrite or organize your work flow before finalizing it into a public-facing repository history.

# Modify the file again
echo "print('Update')" >> app.py

# Stage only the change, then check the status
git add app.py
git status

# Commit the staged version
git commit -m "Add update print statement"

Synchronizing States and Recovering

Because the Working Tree, Index, and Repository are distinct, you can occasionally find them out of sync, which is where Git's recovery commands become critical. Commands like checkout or reset effectively overwrite the Index and Working Tree using the data stored in the Repository. For instance, if you make a mistake in your Working Tree and have not yet committed it, you can reset your local files back to the state of the last commit by checking out the file from the Repository. This ability to overwrite the current state with a known-good historical state is a direct benefit of the architectural separation between where you work and where you store history. By treating the Repository as the 'source of truth' and the Working Tree as a 'scratchpad', you can aggressively refactor and iterate, knowing that you can revert to any previous state in the repository at any moment. This decoupling is the fundamental reason why Git provides such a robust safety net for developers, enabling deep experimentation while maintaining absolute data integrity.

# Overwrite changes in the Working Tree with the version from the last commit
git checkout -- app.py

# Reset the entire Index to match the last commit while keeping Working Tree changes
git reset HEAD

# Verify that the Index is clean but the Working Tree contains the edits
git status

Key points

  • The Working Tree is your active, local environment where you modify your project files.
  • The Index serves as a critical buffer that allows for the curation of changes before they are committed.
  • The Repository is the permanent, historical record that stores snapshots of your project.
  • Git does not track files in the Working Tree until they have been explicitly added to the Index.
  • A commit is a snapshot of the Index, not a reflection of the current state of your Working Tree.
  • Understanding the separation between layers allows you to fix mistakes before they become part of your history.
  • Data flows from the Working Tree to the Index, and finally into the Repository during a commit.
  • You can synchronize the Index and Working Tree with the Repository to revert unwanted changes at any time.

Common mistakes

  • Mistake: Thinking 'git add' moves files into the remote repository. Why it's wrong: 'git add' only updates the Index (staging area), not the local repository or remote server. Fix: Remember that 'git add' is only the first step; use 'git commit' to move changes to the local repository and 'git push' to move them to the remote.
  • Mistake: Assuming the Index is just a hidden folder. Why it's wrong: The Index (or staging area) is a complex binary file located at .git/index that tracks the tree structure, not just a simple list of files. Fix: Treat the Index as a staging manifest that represents what will be included in the next commit snapshot.
  • Mistake: Expecting that deleting a file in the Working Tree automatically updates the Index. Why it's wrong: Git treats deletions as changes to the Working Tree; if you just delete a file with 'rm', the Index remains unaware until you stage that removal. Fix: Use 'git rm <file>' to simultaneously remove the file from the disk and stage the deletion in the Index.
  • Mistake: Believing that uncommitted changes are safe if the repository folder is copied. Why it's wrong: Files in the Working Tree that have not been staged or committed are not tracked by the Git database and will not be included in a standard repository transfer. Fix: Ensure all important changes are committed before performing backups or transfers.
  • Mistake: Modifying a file after 'git add' but before 'git commit' without re-adding it. Why it's wrong: Git only commits the version of the file that was present in the Index at the time 'git add' was executed. Fix: Always re-run 'git add' if you make changes to a file after staging it to ensure the latest version is captured in the commit.

Interview questions

Can you explain the basic purpose of the Working Tree in Git?

The Working Tree is essentially the project directory on your local machine where you actively edit, create, and delete files. It represents the state of your project at the current moment, independent of Git's internal tracking mechanisms. Its primary purpose is to provide a sandbox where you can modify code without immediately affecting the repository history. By keeping the Working Tree separate from the Git repository, Git allows you to experiment with changes, test new features, or fix bugs, and then explicitly decide which of those changes should be prepared for the next commit. If you delete a file in the Working Tree, Git recognizes that the file has been modified or removed compared to the last commit, but those changes aren't officially recorded until you explicitly act on them.

What is the role of the Index, often called the Staging Area, in Git?

The Index is a crucial middle layer between the Working Tree and the Git Repository. It serves as a staging area where you organize and prepare changes before officially recording them in a commit. The reason this exists is to give you granular control over what goes into your project history. For instance, if you have edited five different files but only want to commit changes related to a single feature, you can add those specific files to the Index using 'git add'. By segregating the preparation of files from the final commit, Git ensures that you don't accidentally include unfinished work or debugging logs. Once files are added to the Index, Git takes a snapshot of them, which becomes the basis for the next commit.

How does the Git Repository differ from the Working Tree and the Index?

The Git Repository is the core of Git's architecture; it is stored inside the hidden .git directory. While the Working Tree and Index are temporary representations of your files, the Repository is the permanent, persistent record of your project's history. It holds all the commit objects, branch pointers, and tags that make up your history. The reason we distinguish the repository is to maintain integrity and allow for non-linear development. When you run 'git commit', Git takes the snapshot stored in the Index and creates a permanent record within the Repository. Because the Repository is separate, you can switch between different branches or time-travel to old commits without worrying about losing the snapshots of previous states, as they are safely encoded in the Git database.

Can you compare the process of using 'git add' versus 'git commit' in terms of the architecture?

The fundamental difference between these two commands lies in which architectural layer they interact with. 'git add' is the bridge between the Working Tree and the Index. It updates the Index by taking the current state of files in the Working Tree and staging them for the next commit. This is an additive process that allows you to build a snapshot incrementally. Conversely, 'git commit' is the bridge between the Index and the Repository. It takes the entire collection of staged changes currently held in the Index and bundles them into a permanent, immutable commit object within the Repository. You use 'git add' to curate content, and 'git commit' to solidify that content into the project's historical record, effectively locking in that snapshot forever.

Explain the concept of an 'unstaged change' versus a 'staged change' using the Index architecture.

An unstaged change occurs when you modify a file in your Working Tree, but you have not yet run 'git add' to inform the Index about these specific modifications. Git can see these differences via 'git status', but they are not currently part of the next scheduled snapshot. A staged change, however, is a file that has been added to the Index, meaning its current content matches the version that will be saved in the next commit. The reason this distinction is vital is that it allows developers to perform 'partial commits'. For example, if you edit a file but only want to save half your work, you can stage only the relevant hunks using 'git add -p'. This architectural separation protects the integrity of your commits by forcing you to consciously choose what is ready to be finalized.

If you perform a 'git reset --soft' versus a 'git reset --hard', how do these affect the three main areas of Git?

The 'git reset' command behaves very differently depending on the mode you select because it targets different layers of the architecture. A '--soft' reset only moves the branch pointer in the Repository back to a previous commit, leaving both your Index and your Working Tree untouched. This is useful when you want to undo a commit but keep your work prepared for a new commit. A '--hard' reset is more destructive; it forces the Index and the Working Tree to match the state of the Repository at a specific commit. This means any uncommitted changes in your Working Tree or staged changes in your Index are permanently overwritten or discarded. You must understand these three areas to use 'git reset' safely, as the '--hard' option fundamentally overwrites your current work to match the historical record stored in the repository.

All Git & GitHub interview questions →

Check yourself

1. If you modify a file in your Working Tree and run 'git commit', why does Git not include your changes in the commit?

  • A.The file was not added to the Index.
  • B.The file is currently locked by the operating system.
  • C.The Working Tree is not part of the Git architecture.
  • D.Git requires you to push before committing locally.
Show answer

A. The file was not added to the Index.
Git creates commits based solely on the current state of the Index, not the Working Tree. The other options are incorrect because the file being locked has no effect on Git's logic, the Working Tree is central to Git's architecture, and pushing is unrelated to the commit process.

2. What is the primary function of the Index (staging area) in the Git architecture?

  • A.It stores a backup of every version of every file.
  • B.It acts as a buffer to prepare the exact snapshot for the next commit.
  • C.It synchronizes local changes with the remote server automatically.
  • D.It serves as the main database for storing history.
Show answer

B. It acts as a buffer to prepare the exact snapshot for the next commit.
The Index allows the user to carefully craft a commit snapshot. Storing backups, syncing with remotes, and acting as a history database are roles of the repository object database, not the staging area.

3. How does the 'git checkout' command impact the three areas of Git?

  • A.It updates the Repository and the Index simultaneously.
  • B.It only modifies files in the Index without changing the disk.
  • C.It updates the Working Tree and the Index to match a specific commit.
  • D.It forces the remote server to delete files in the Working Tree.
Show answer

C. It updates the Working Tree and the Index to match a specific commit.
When checking out a branch or commit, Git overwrites the Index and the Working Tree to align with the state saved in the repository. The other options are wrong because 'git checkout' focuses on local state, not the remote, and it must affect the Working Tree to be useful.

4. After performing a 'git add' on a file, how can you revert the changes in the Index without losing the changes in your Working Tree?

  • A.Use 'git reset HEAD <file>'.
  • B.Use 'git rm --cached <file>'.
  • C.Use 'git commit --amend'.
  • D.Use 'git clean'.
Show answer

B. Use 'git rm --cached <file>'.
'git rm --cached' removes the file from the Index while leaving the file on disk. 'git reset' is often used but 'git rm --cached' is the explicit command for this; 'git commit --amend' affects history, and 'git clean' removes untracked files.

5. Which command best describes the movement of data from the Working Tree into the Repository?

  • A.git add followed by git commit
  • B.git push followed by git add
  • C.git commit followed by git push
  • D.git checkout followed by git stage
Show answer

A. git add followed by git commit
Data moves from the Working Tree to the Index via 'git add', then from the Index to the Repository via 'git commit'. The other sequences are logically incorrect steps for moving data into the local history database.

Take the full Git & GitHub quiz →

Next →init, clone, add, commit, status, log

Git & GitHub

20 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app