05 — Version Control — An Undo Button for Whole Teams
"Save my files somewhere safe before I change them" was my version-control plan, and it was a backup, not version control. The real model: version control is an undo button that works for whole teams. It snapshots history so every change is reversible, and so multiple people can change the same project at the same time without overwriting each other [1].
The thing I had to see clearly is what a Git repository actually is. I spent a long time thinking of it as a folder with a memory, when the model that fits is this: a repo is a graph of commits, where each commit is a snapshot of the whole project, a branch is just a movable label pointing at one commit, and merging is rejoining two paths that diverged in that graph [2]. Once that picture landed, checkout, branch, merge, and revert stopped being incantations and became obvious operations on the graph.
Here's that graph — one line of commits that forks into two branches and then merges back — which is the picture I keep in my head for everything that follows:
Why version control at all
Before I used Git, my "version control" was project_final_v2_REAL_final.zip. It worked until two things happened at once: I wanted to try a risky change without wrecking the working version, and someone else needed to edit the same files. Copying folders solves neither cleanly [1].
A VCS fixes both by storing a history of snapshots of the project, not the current files. Every saved state — a commit — is a complete record of what the project looked like at one point, with a message explaining why it changed. The payoff is two-fold [3]:
- Reversibility. Any change can be undone, because every previous state still exists in the graph. "I broke it" stops being scary when the fix is "move the label back one commit."
- Parallelism. Two people can branch off the same point, work independently, and rejoin later without one clobbering the other's work. That's the "for whole teams" part of the undo button.
This is the layer that makes modern collaboration possible — code review, pull requests, releasing a fix while a feature is half-built, all rest on it. Without it you're back to shouting across the room before touching a file.
The three states — where files actually live
Git confused me for a long time because files seem to be in several places at once. The model that cleared it up is that a file is always in one of three states, and the commands are mostly about moving files between them [4]:
The two-step mattered more than I expected. git add doesn't save anything to history — it just says "these edits are the ones I intend to save next." That lets me stage three files, leave a fourth half-finished in the working directory, and commit exactly the three. git commit is the act that actually writes a snapshot into the graph. Confusing the two is why beginners think they "saved" a change that isn't in any commit.
Commits as snapshots, not diffs
The other thing I had to unlearn: a commit is not a diff (a list of lines that changed). Internally Git stores it as a full snapshot of the project at that moment — it just compresses and dedupes unchanged files so storing thousands of snapshots stays cheap [4]. The diff is derived afterward when you ask Git to compare two commits.
This is why reverting is clean. Because each commit is a complete state, moving to any past commit puts the project back exactly as it was, no matter how tangled the changes look in diff form. The history is a series of complete photographs, and the "what changed" view is computed by holding two photographs side by side.
Branching and merging — labels on a graph
This is where the graph model earns its keep. A branch in Git is not a copy of the project and not a folder. It's a tiny pointer — a label — that names one commit, the tip. When I commit on a branch, Git creates a new commit whose parent is the current tip, and slides the label forward to the new commit [2]. That's it. Branches are cheap because they're just labels; the project data is shared across all of them.
Merging is the act of rejoining two paths that diverged. Two branches share a common ancestor; each has moved on since. A merge produces a commit with two parents — the tips of both branches — combining both lines of work into one new state (the merge commit M in the figure above). Most of the time Git works out the combination automatically; when the same line was edited on both branches it pauses and asks me to resolve the conflict by hand, picking the intended result [3].
The mental shift was treating branches as questions about the graph rather than as separate folders. "Create a branch" is "make a new label here and move it as I commit." "Switch branches" is "move my working directory to the commit this other label points at." The graph never duplicates; only the labels move.
Local and remote — the distributed split
The last piece is why Git is called distributed. A Git repo is fully functional on my laptop with no network connection — every commit, branch, and the entire history live locally in the .git directory [5]. I can branch, commit, and merge all day offline.
A remote is just another copy of the same repo, usually on a host like GitHub. git push sends my local commits to the remote; git pull fetches the remote's new commits into my local graph [5]. The remote is not the source of truth in the way a central database is — it's a peer that the team has agreed to sync through. That's why I can commit on a plane and push when I land, and why "the server is down" never blocks me from saving work to history.
This local-vs-remote split is the concrete shape of the "undo button for whole teams." History lives on every machine, so it's durable; the remote is the shared meeting point where parallel histories get synced and merged back together.
How I use this
The payoff of the graph model is that the commands stopped feeling arbitrary. When something goes wrong — I committed to the wrong branch, or I want to throw away the last hour — I now ask what shape does the graph have, and what shape do I want? Most operations are then obvious: undo a commit by moving the label back, combine branches by merging, experiment safely by making a new label and committing there. The history is a graph I can walk in any direction, and branches are just the names I gave to the spots worth coming back to.
References
[1] Atlassian, "What is version control?," Git Tutorials, 2024. [Online]. Available: https://www.atlassian.com/git/tutorials/what-is-version-control
[2] S. Chacon and B. Straub, "Getting Started — What is Git?," Pro Git, 2nd ed., 2024. [Online]. Available: https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F
[3] Atlassian, "Git branching," Git Tutorials, 2024. [Online]. Available: https://www.atlassian.com/git/tutorials/using-branches
[4] S. Chacon and B. Straub, "Recording Changes to the Repository," Pro Git, 2nd ed., 2024. [Online]. Available: https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
[5] Atlassian, "Setting up a repository — What is a version control repository?," Git Tutorials, 2024. [Online]. Available: https://www.atlassian.com/git/tutorials/setting-up-a-repository
[6] "Why version control?," inter-git.com, 2024. [Online]. Available: https://inter-git.com/lessons/introduction
Knowledge check · Question 1 of 5
A Git branch is best described as…
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!