Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Git Foundations: Working Tree, Index, and Commits

Build the core Git mental model by creating a repository and observing the real state transitions from working tree to index to commit and history.

Levels: Beginner ~18 min read

Basic

Git stores project history as states

Git is a distributed version control system. A repository records project history as a graph of commits, and each commit identifies a project state. This lesson starts with the smallest useful workflow: create a repository, inspect it, stage a file, commit it, and inspect the resulting history.

The important model is not a list of commands. Your working tree contains the files you are editing. The index records the content selected for the next commit. A commit records a tree describing a project state and links that state into history. Git’s official user manual describes high-level operations as moving data among the working tree, index, and object database.

Create a repository

Start with an empty directory and initialize a repository. The -b main option requests the initial branch name when creating the repository.

bash
mkdir git-practice
cd git-practice
git init -b main

After initialization, Git creates repository metadata in .git. There are no commits yet. The current branch name is configured, but it is an unborn branch until the first commit exists.

bash
git status
git symbolic-ref --short HEAD

Quick check

Immediately after `git init -b main`, before the first commit, what does `HEAD` normally identify?

git init -b maingit symbolic-ref --short HEAD

The working tree and status

Create a file, then ask Git for its status. The file exists in the working tree, but it is not yet in the index.

bash
printf "Hello, Git\n" > README.md
git status --short

The short status output should report README.md as untracked. At this point there is nothing staged for the next commit.

bash
?? README.md

Quick check

A newly created file that Git has not been told to stage is in which state?

Stage content in the index

Use git add to update the index with the file’s current contents.

bash
git add README.md
git status --short

The status should now show an added file in the index. The working tree and index agree for this path, so a plain git diff has nothing to show.

bash
A  README.md
git diff
git diff --cached

The distinction matters: git diff compares working-tree changes with the index, while git diff --cached compares the index with HEAD. For a new file before the first commit, the cached diff shows the staged addition.

Quick check

After `git add README.md`, which command compares the staged content with the current `HEAD`?

Commit the staged state

A commit records the state represented by the index. Create the first commit with a message.

bash
git commit -m "Add README"

After a successful commit, the repository has a commit reachable from the current branch. The working tree and index are clean for the file you just committed.

bash
git status --short
git log --oneline -1
git rev-parse HEAD

Quick check

What does the new commit primarily take its project-state contents from?

Change, compare, then stage again

Now change the working-tree file without staging the change. This creates a useful state boundary: the commit and index still describe the old content, while the working tree contains the new content.

bash
printf "Hello, Git!\n" > README.md
git status --short
git diff

At this point git diff shows the unstaged change. If you stage the change, the ordinary diff becomes empty because the working tree and index agree again; the staged difference moves to git diff --cached.

bash
git add README.md
git diff
git diff --cached

Inspect the history

Use git log to inspect commits and git show to inspect a particular commit. rev-parse can resolve HEAD to its object ID, which is useful when verifying exactly which commit a branch points to.

bash
git log --oneline
git show --no-patch --format=fuller HEAD
git rev-parse HEAD

A commit is not merely a line of text. Git’s official model describes a commit object as referring to a tree representing a project state and to parent commit(s) that establish its history.

Quick check

Which command is most directly used here to inspect the commit currently named by `HEAD`?

The complete first workflow

The core workflow can now be read as state transitions rather than memorized commands.

bash
working tree
    |
    | git add
    v
  index
    |
    | git commit
    v
  commit
    |
    | git log / git show
    v
  history

This model is the foundation for later Git topics. Branches, merges, remotes, rebases, recovery, and Git’s object model all become easier to reason about once these state boundaries are explicit.

Practice what you learned

Use the dedicated Git code-completion exercise for this lesson.

Was this page helpful?

Mark this learning resource complete to track your learning progress.