RizTech Academy logo
RizTech Academy
Git and CollaborationLesson 1 of 530 min

Git’s mental model: commits, branches, HEAD

Most people learn Git as four memorised commands — add, commit, push, pull — and are lost the moment anything goes wrong, because they never built a picture of what those commands actually do. This module fixes that. If you understand Git's mental model — what a commit is, what a branch is, what HEAD points at — then branching, merging, resolving conflicts and undoing mistakes all become obvious rather than terrifying. This first lesson builds that model. It assumes you have used git a little; the goal is to make it make sense.

Git stores snapshots, not changes

The first idea, and the one that unlocks everything else: a commit is a snapshot of your entire project at a moment in time, not a "change" or a "diff". When you commit, Git records what every tracked file looked like right then. (It stores this efficiently under the hood, but the mental model is: each commit is a full snapshot.) A project's history is therefore a sequence of snapshots, each pointing back to the one before it — its parent.

Each commit has:

  • A unique hash (a long id like a1b2c3d) — how you refer to it.
  • A parent (the commit before it), which links the snapshots into a chain.
  • The snapshot of all files, plus a message, author and timestamp.

So the history is a chain of commits, newest pointing back to oldest. Picturing commits as snapshots linked by parent pointers is the foundation; hold it and the rest follows.

The three areas: working directory, staging, repository

Git has three places your files can be, and understanding them explains add and commit:

  • The working directory — your actual files on disk, as you edit them.
  • The staging area (the "index") — a holding area where you assemble the next commit. git add moves changes here.
  • The repository — the committed history. git commit takes what is staged and records it as a new snapshot.

So the flow is: edit files (working directory) → git add chosen changes (staging) → git commit (repository). The staging area is the bit beginners find odd, but it is useful: it lets you commit some of your changes and not others, so each commit is a coherent, single idea rather than a dump of everything you touched. Check where things are with:

git status         # what is modified, staged, untracked — read this constantly
git diff           # changes in the working directory, not yet staged
git diff --staged  # changes that ARE staged, i.e. what your next commit will contain

git status is the command you run more than any other — it always tells you the current state and often what to do next.

Branches are just pointers

Here is the idea that makes branching un-scary: a branch is simply a movable pointer to a commit. Not a copy of your files, not a heavy thing — just a label pointing at one commit. main is a pointer; when you commit on main, the pointer moves forward to the new commit. Create a branch and you create another pointer at the same commit:

git branch feature      # create a new pointer 'feature' at the current commit
git switch feature       # move onto it (older syntax: git checkout feature)
git switch -c feature    # create and switch in one step

Because a branch is just a cheap pointer, creating one is instant and costs nothing — which is why Git workflows use branches liberally (the branching lesson). When you understand that a branch is a pointer, "make a branch, work on it, merge it" stops being mysterious: you are just moving pointers around a graph of snapshots.

HEAD: where you are

One more pointer: HEAD points at where you currently are — normally at the branch you are on (which in turn points at a commit). So HEAD → main → a1b2c3d means "I am on main, which is at commit a1b2c3d". When you switch branches, HEAD moves to point at the other branch. When you commit, the current branch pointer (and HEAD with it) moves to the new commit.

This lets you refer to commits relative to where you are, which is enormously useful:

HEAD        # the current commit
HEAD~1      # one commit before HEAD (its parent)
HEAD~2      # two commits before

You will use HEAD~1 constantly for "the previous commit" — when undoing, comparing, or resetting (the undoing lesson). Knowing HEAD is "you are here" makes those commands readable.

Why this model makes everything else easy

Everything in the rest of this module is just moving these pointers over the graph of snapshots:

  • Branching — make a new pointer and commit on it.
  • Merging — combine two branches' histories, moving a pointer to a new commit that has two parents.
  • Conflicts — happen when the same lines changed on both branches, so Git cannot auto-combine the snapshots.
  • Undoing — move HEAD or a branch pointer to an earlier commit (reset), or make a new commit that undoes an old one (revert).

None of that requires memorisation once you see it as pointers over a commit graph. That is the whole reason to learn the model first: Git stops being a set of spells you fear breaking and becomes a small, logical system you can reason about — which is exactly what you need when something goes wrong on a real project under pressure.

Check your work

Commits are snapshots, not diffs: each records the whole project at a moment, with a hash (id), a parent (the commit before), a snapshot, and metadata. History is a chain of snapshots linked by parent pointers.

Three areas: working directory (files on disk) → git add → staging/index (assembling the next commit) → git commit → repository (history). Staging lets each commit be one coherent idea. Read state with git status; git diff (unstaged) / git diff --staged (what the next commit holds).

A branch is a movable pointer to a commit — cheap and instant, not a copy. Committing moves the pointer forward; git branch/git switch -c create one. This is why branching is used liberally.

HEAD = "you are here" — points at your current branch → current commit. HEAD~1/HEAD~2 = previous commits. Switching moves HEAD; committing moves the branch (and HEAD) to the new commit.

Everything else is moving pointers over the snapshot graph — branch, merge (a commit with two parents), conflict (same lines changed both sides), undo (reset moves a pointer; revert adds an undoing commit). The model removes the fear.

Practice

  1. In a repo, edit a file, run git status, git add it, run git status again, and git commit; describe what moved between the three areas.
  2. Use git diff and git diff --staged and explain the difference between what each shows.
  3. Run git log --oneline and explain how the commits chain together via parents.
  4. Create a branch with git switch -c, commit on it, and explain what happened to the branch pointer and HEAD.
  5. Explain, in your own words, why "a branch is just a pointer" makes creating branches cheap.
  6. Use git show HEAD and git show HEAD~1 and explain what HEAD and HEAD~1 refer to.

Official documentation

Next: branching and merging.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship