Topic · Foundations & Daily Workflow

Repositories & the Working Tree

Git gets much easier once you stop thinking of it as one place. A project has a working tree you edit, a repository that stores history, and an index that lets you choose exactly what the next commit will contain.

The three-place mental model

When you ask Git what changed, it is not comparing a file against one single truth. It is comparing three places: the last committed snapshot, the staged snapshot you are preparing, and the files currently on disk. Most beginner confusion comes from mixing those places together.

The important word is snapshot. A commit is not a running diary of every keystroke. It is a recorded project state. The index exists so you can build that state deliberately, even when your working tree contains extra experiments, debugging notes, or half-finished edits.

Repository versus working tree

A working tree is the ordinary directory tree where your project files appear. If you open README.md, change a function, or delete a generated file, you are changing the working tree.

Repository

A Git repository is the hidden control data that stores objects, refs, configuration, and the index. In a normal non-bare project, it lives in the .git directory at the project root.

That difference explains why deleting a file from your editor does not instantly erase history. You removed a file from the working tree. The repository still has previous snapshots until you intentionally stage and commit the removal. It also explains why a folder can contain project files but not yet be a Git repository: without .git, Git has nowhere to store history or compare states.

Check whether this folder is inside a work treeshell
git rev-parse --is-inside-work-tree
git status

If the first command prints true, Git found repository metadata above or at the current directory. If git status says the directory is not a Git repository, you are outside a work tree or inside a directory that has not been initialized.

What lives in .git

The .git directory is not a backup folder full of duplicate project files. It is Git's database and control center. You rarely edit it by hand, but knowing the landmarks makes Git feel less mysterious.

Path Role
.git/objects/ Stores Git objects: file contents, directory trees, commits, and tags.
.git/refs/ Stores names that point to commits, such as branches and tags.
.git/HEAD Records what is currently checked out, usually by pointing to the current branch ref.
.git/index Stores the staging area, also called the index.
.git/config Stores repository-specific configuration, such as remotes and local settings.
Practical boundary

It is useful to inspect .git, but let Git commands mutate it. A hand-edited ref, index file, or object file can put the repository into a state that is harder to reason about than the original problem.

Objects, commits, and HEAD

At a high level, Git is a content-addressed database. Content goes in; Git computes an object ID from that content; later, other objects and names can point to that ID. You do not need internals fluency for daily work, but the basic object shapes matter.

Blob

File contents, without the filename. Two files with identical contents can share the same blob.

Tree

A directory snapshot: filenames, modes, and links to blobs or other trees.

Commit

A project snapshot plus metadata: parent commit, author, committer, time, and message.

HEAD is your current position. In normal branch-based work, HEAD points to a branch, and the branch points to the latest commit on that branch. When you commit, Git writes a new commit object from the index, then moves the branch forward so HEAD sees the new snapshot.

Inspect the current positionshell
git status --short --branch
git log --oneline -1
git rev-parse --abbrev-ref HEAD

That last command usually prints your branch name. In a detached-HEAD state it prints HEAD, which means you checked out a commit directly rather than a branch name. Detached HEAD is not automatically bad, but it changes where new commits will be attached.

The index and staging area

The index is the most important Git idea that most tools hide. It is a file inside .git that records what the next commit will look like. The phrase staging area is the friendly name for the same idea.

git add does not mean "start tracking forever" in every situation. More generally, it updates the index using content from the working tree. If a file is new, that can make the file tracked. If a file is already tracked, it stages the current version. If a tracked file was deleted, git add can stage that deletion.

Stage one exact fileshell
git status
git add docs/intro.md
git status

After the git add, Git may still report changes in the same file under "not staged" if you edited it again. That is not a contradiction. The index contains the version you staged; the working tree contains a newer version. One path can be both staged and modified at the same time because Git is comparing different pairs of snapshots.

Question Comparison Git makes
What is staged? HEAD versus the index
What is unstaged? The index versus the working tree
What will commit? The contents currently recorded in the index

Tracked and untracked files

A tracked file is known to Git because it is in the last commit, in the index, or both. An untracked file exists in the working tree but is not part of Git's known snapshots. Git sees it, but it will not include it in a commit unless you stage it.

Short status vocabularyshell
git status --short
# ?? notes.txt       untracked
#  M app.js          modified in working tree
# M  README.md       modified in index
# MM package.json    staged version plus newer unstaged edits

This is why git status is the safest habit in early Git work. It tells you which files are untracked, which changes are staged for commit, and which tracked files have working-tree edits that are not staged yet.

Daily habit

Run git status --short before and after staging. You are checking whether your mental model and Git's three-place model agree.

Init versus clone

There are two common ways to get a local repository. git init turns an existing directory into a new repository. git clone copies an existing repository from somewhere else and checks out a working tree for you.

Use git init when...

You are starting history for a local project that is not yet under version control.

cd my-project
git init
git add README.md
git commit -m "Initial commit"
Use git clone when...

The project already exists somewhere else, such as a hosting service or another server.

git clone https://github.com/libgit2/libgit2
cd libgit2
git status

Both routes produce a repository and a working tree on your machine. The difference is the source of the history. With init, the first commit is yours to create. With clone, the object database, refs, remote configuration, and checked-out files are copied from an existing project.

Common pitfalls

Pitfall 1

Assuming saved means committed. Saving a file only changes the working tree. The repository does not record that change until you stage it and commit it.

Pitfall 2

Staging too broadly. git add . is convenient, but it can sweep in generated files, debug edits, or secrets. In early practice, prefer exact paths or review with git status --short immediately afterward.

Pitfall 3

Deleting .git to "clean up." That removes the local repository metadata: history, branches, index, remotes, and configuration. Your visible files may remain, but the folder is no longer the same Git repository.

Pitfall 4

Forgetting that one file can be in two states. A file can have a staged version and newer unstaged edits. If you commit, Git records the staged version, not necessarily what your editor currently shows.

Worked examples

Example 1: You edited a file, but Git says nothing is staged

You changed app.js and saved it. git status shows it under changes not staged for commit.

git status --short
#  M app.js
git add app.js
git status --short
# M  app.js

The first status means the working tree differs from the index. After git add, the index differs from HEAD, so the change is ready for the next commit.

Example 2: The same file is staged and modified

You staged README.md, then edited it again before committing.

git status --short
# MM README.md

The left column says the index has changes compared with HEAD. The right column says the working tree has changes compared with the index. If you commit now, Git records the staged version only. Run git add README.md again if you want the newer edit included.

Example 3: You created a new file and Git calls it untracked

A new file starts outside Git's snapshots.

touch notes.txt
git status --short
# ?? notes.txt
git add notes.txt
git status --short
# A  notes.txt

?? means Git sees the file but will not commit it. A in the index column means the file is staged as an addition.

Example 4: You initialized an existing folder

git init creates the repository metadata, but it does not automatically commit your existing files.

cd recipe-site
git init
git status --short
# ?? index.html
# ?? styles.css

At this point, the repository exists, but the project files are still untracked. Stage the files you want in the first snapshot, then commit them.

Example 5: You cloned a project and immediately have files

git clone copies repository data and checks out a working tree.

git clone https://github.com/libgit2/libgit2
cd libgit2
git status --short --branch

Unlike a fresh git init, the clone already has history and a current branch. The files you see are the checked-out snapshot from that branch.

Sources & further reading

  • Pro Git: Getting a Git Repository Textbook Scott Chacon and Ben Straub · Pro Git, 2nd ed.

    Primary reading for the difference between creating a repository with git init and copying one with git clone.

  • Pro Git: Recording Changes to the Repository Textbook Scott Chacon and Ben Straub · Pro Git, 2nd ed.

    The core source for tracked, untracked, staged, and modified file states in everyday Git work.

  • git-init documentation Reference Git project

    Formal command reference for initializing repositories, including options that affect the repository layout.

  • git-status documentation Reference Git project

    Precise reference for status output, short format, branch display, and how Git reports index and working tree differences.

  • git-add documentation Reference Git project

    Formal reference for updating the index with working tree content, including additions, modifications, and removals.

  • Pro Git: Git Objects Textbook Scott Chacon and Ben Straub · Pro Git, 2nd ed.

    A deeper explanation of Git's object database: blobs, trees, commits, and how the index becomes a tree.