Why Version Control Exists
What Git actually stores and why its workflow is shaped the way it is: snapshots, commit hashes, history as a graph, and why every clone is a full copy.
What Git actually stores and why its workflow is shaped the way it is: snapshots, commit hashes, history as a graph, and why every clone is a full copy.
Somewhere on nearly every hard drive there is a folder like
this: site/, site_v2/, site_v2_new/, site_v2_new_ACTUAL/,
and a zip called backup_before_i_broke_it.zip. It works the way
juggling works — fine, right up until the moment you drop
something.
By the end of this lesson you will know what Git actually stores when you save your work, why its history is shaped like a chain rather than a list, and why every workflow in the lessons ahead looks the way it does. There is almost nothing to type here. This is the mental model the rest of the course hangs from, and it is worth more than any command.
Copying the folder feels like it should be enough. It is not, and it fails in three different ways that have nothing to do with being disorganised.
A working version is not one good file.
A project is dozens of files that depend on each other. It works when they all agree with each other at the same moment. Copy half of them into a new folder and you have preserved nothing at all.
You changed six things. Which one did it?
The page is blank and there is no record of what moved. Without one, the only way to find out is to undo your own work from memory and hope you remember correctly.
Somebody's afternoon disappears.
You and a colleague both open checkout.js on Monday. By
Friday you each have a version you like. Email the files
around and one of you loses a day — and nobody finds out
until a customer does.
Version control is the class of tool that solves all three at once, and Git is the one the industry settled on. It keeps a complete, searchable record of every state your project has ever been in, and lets many people change that project without stepping on each other.
A commit is the unit Git works in, and it holds five things.
the whole project, exactly as it looked at one moment
name and email
a timestamp
its parent — this is what makes history a chain
the one part Git cannot work out for itself
Read the top row again, because it is where most beginners guess wrong. A commit is not a list of edits. It is the whole project, whole, at a point in time.
Think of a manuscript editor who photocopies the entire book at the end of each working day and files the stack in a cabinet. Nothing in the cabinet says "changed page 40". Every drawer just holds a complete book.
So where do the red and green change listings come from? Git works them out on demand. Ask what changed between Tuesday and Wednesday and Git pulls both snapshots and compares them — the way that editor would hold two photocopied pages up to the light. The diff you see on screen is a result, not a stored thing.
Rebuild each version by replaying the ones before.
How several older tools work. Version 40 exists only if every one of the 39 changes before it replays correctly, so history is slow to walk and one corrupt step poisons everything after it.
Compute the edits whenever somebody asks.
How Git works. Every version stands on its own, so history is fast to browse and hard to corrupt. Unchanged files are not stored twice — both snapshots point at the same stored copy, and it is all compressed.
Every commit gets a name like this:
e83c5163316f89bfbde7d9ab23ca2e25604af290That is a hash — forty hexadecimal characters, usually shown
as just the first seven (e83c516), which is enough to be
unique in most projects. It is not random and it is not a
counter. Git calculates it from the contents of the commit: the
files, the author, the timestamp, the message, and the parent.
That calculation buys two things.
The first is integrity. Change one character in one file and the hash changes completely. A commit's name is therefore a verifiable claim about its contents — nobody can quietly edit history underneath you, because edited history has different names.
The second is unambiguous naming. "Version 3" means different
things to different people. e83c516 means precisely one
snapshot on every machine in the world that has it. When you
tell a colleague which commit broke the build, there is nothing
left to interpret.
Because every commit records its parent, commits link backwards into a chain. That is what makes history navigable: from any commit, Git can walk back through its ancestors all the way to the first one. A log is that walk, printed:
a3f19c2 Add a search box to the site header
7e0b4d8 Extract the header into its own component
1c9f60a Fix the footer link colour
e83c516 Initial commitThat comes from git log --oneline, which you will use
constantly from the next lesson on. Newest at the top. Each line
is one snapshot, named by its short hash and described by its
message.
A straight line is only the simple case. A commit can have more than one child, so two commits can point back at the same ancestor and history becomes a family tree rather than a queue:
A---B---C---F <- main
\
D---E <- add-searchRead it left to right as time. A is oldest. B has two
children: C, which carried on along main, and D, which
started a separate line of work called add-search. Both lines
are real history and both exist at once. The names on the right
are branches — nothing more than labels pointing at the
newest commit on each line.
One detail worth holding onto: the arrows in Git point the
opposite way from how you read that picture. E knows it came
from D; D knows it came from B. No commit knows its
children. That sounds like a limitation and is actually the
reason a commit's hash can be fixed forever — nothing that comes
later can change it.
Git is distributed. When you copy a project from somewhere
else — the operation is called a clone — you do not get the
latest files. You get the entire repository: every commit, every
branch, the full history, sitting in a hidden .git folder
inside your project.
Older centralised tools such as Subversion worked differently. One server held the history and your machine held a working copy of a single version. Viewing last year's changes, comparing two releases, or committing at all meant a round trip to that server — and if the server was down, or you were on a train, you waited.
This is why Git feels quick. Browsing history, switching branches and comparing versions all read from your own disk, at local speed, with no network at all. You need a connection for exactly two things: sending your commits out and fetching other people's. It is a resilience story too — every clone is a complete backup of the project's history.
These two get conflated constantly, and untangling them now saves confusion later. Git is the program on your computer that manages commits. GitHub is a website that stores a copy of a Git repository and adds things Git itself has no opinion about: issue tracking, pull requests, permissions, and a browsable interface.
Because clones are complete, hosting is a convenience rather than a dependency. You could use Git for years entirely offline. Most people do not, because having one agreed copy that everyone syncs with is a sane way to work together.
Since a commit is a snapshot with a reason attached, the craft is in choosing what belongs in one. A good commit contains one coherent change: the fix, plus the test for the fix, and nothing else. Rename fifteen variables and fix a bug in the same commit and you have made a snapshot nobody can undo, review, or explain.
The message matters just as much, and the useful half of it is the why. "Update user.js" tells your future self nothing the diff does not already show. "Reject sign-ups with blank email so the welcome job stops failing" tells them the thing the code cannot: what problem this was solving.
This lesson had no commands to collect, so the cheat sheet is a vocabulary list instead. These are the terms the rest of the course assumes you know; come back whenever one stops feeling solid.
repository # a project folder plus its complete history
commit # a snapshot of the whole project, plus why
commit message # the note explaining the reason for a commit
hash / commit ID # the long hex name computed from the content
parent # the commit that a given commit came from
branch # a movable label naming one line of history
main # the branch most projects treat as the trunk
HEAD # a pointer to the commit you are sitting on
working directory # your files on disk, as they are right now
diff # the difference Git computes between snapshots
clone # a full copy of a repository, history and all
remote # another copy of the repo, usually on a server
origin # the customary nickname for your main remote
merge # joining two lines of history back togetherYou now have the model: Git stores whole snapshots, names each one by a hash of its contents, and chains them through parent links into a graph you can walk. Every command in this course is some way of creating, naming, moving between, or comparing those snapshots. When a command confuses you later, come back and ask what it is doing to the graph.
Next is Installing Git, where you get the tool onto your machine and tell it your name and email — because a commit records an author, and Git refuses to guess who you are. After that you will create a repository and make a real commit of your own.
Before you move on, try describing to yourself, out loud, what a commit contains and why its ID is a hash. If both answers come easily, the rest of Git will feel far less arbitrary than it does to most people learning it.