1.4.11 · D3Python & Scientific Computing

Worked examples — Git version control basics

5,016 words23 min readBack to topic

This page is the Git basics case-book. The parent note taught the machinery — repository, commit, branch, staging, remote. Here we do the opposite: we list every kind of situation Git can put you in, then work one example per situation so that when the real thing happens on your keyboard, you have already seen it.

Before we start, one honesty note. Git is not geometry — there are no angles or quadrants. But it does have "case classes" that behave exactly like signs and edge cases in maths: the merge that fast-forwards vs the merge that must build a new commit, the clean pull vs the conflicting pull, the empty repo vs the detached HEAD. Those are our quadrants. We enumerate them all.


The scenario matrix

Read the table as: "which shape can the repository be in right now, and what does the command do in that shape?" Every one of these nine cells is worked in full below on this page — the rightmost column tells you which example (all on this page) covers it.

Cell Situation (the "sign" of the repo) The degenerate / edge twist Example (this page)
A Brand-new folder, no history at all Nothing staged refusal + --allow-empty exception Ex 1
B Linear history, one branch moves forward Amending the last commit Ex 2
C Two branches, only one moved Fast-forward merge (no new commit) + --no-ff twist Ex 3
D Two branches, both moved Three-way merge (new merge commit) Ex 4
E Two branches edited the same line Merge conflict — Git refuses, you decide Ex 5
F Remote ahead of you and remote behind you rejected push vs clean push (both quadrants) Ex 6
G You checked out an old commit directly Detached HEAD — commits with no branch Ex 7
H Real-world word problem Committed a secret / big file by mistake Ex 8
I Exam-style twist "Undo" family: reset vs revert vs checkout Ex 9
Figure — Git version control basics

What the figure above shows (this is figure s01, the first step figure on the page): three mini-graphs left to right. On the left, two blue circles C1 -> C2 joined by one arrow — a plain linear chain (cells A, B), just one pointer walking forward. In the middle, a blue ancestor P with a single green child F1 — only one branch moved, so a merge here is a fast-forward. On the right, ancestor P splits into a red M1 (main moved) and a yellow F1 (feature moved), and both feed a white MG node — the diverged shape that forces a three-way merge. Trace the arrows with your finger: every real repo situation is one of these three pictures.


Cell A — the empty repository


Cell B — linear history, and amending


Cells C & D — merges (the heart of the matrix)

Figure — Git version control basics

What the figure above shows (figure s02): two scenes split by a dashed line. Left (fast-forward): blue P -> A -> B in a straight line, with a green arrow underneath sweeping the main label all the way to B — nothing new is built, the pointer simply slides. Right (three-way): blue ancestor P branches into red M and yellow F, and a white MG node draws its two arrows back to both — that white node is the newly minted merge commit with two parents. The contrast to burn in: left creates zero commits, right creates one.


Cell E — the merge conflict

What the figure above shows (figure s03): a blue base (lr=.01) node splits into a red main (lr=.001) node above and a yellow feat (lr=.1) node below — both edited the same line. On the right sits a dark box outlined in red titled "CONFLICT in config.txt" containing the literal marker block Git writes into your file: <<<<<<< HEAD (your main side, red), a ======= divider, then the feature side (yellow), closed by >>>>>>> feature. The green caption underneath — "you delete markers, add, commit" — is the human step. The picture's message: Git does not guess between 0.001 and 0.1; it hands you the file to decide.


Cell F — remote ahead and remote behind


Cell G — detached HEAD


Cell H — the real-world word problem


Cell I — the exam twist: reset vs revert vs checkout

What the figure above shows (figure s04): the same blue chain C1 -> C2 -> C3 drawn once, with three different undo actions branching off it. revert (green) adds a new node C4 undo to the right — history grows forward. reset --hard C2 (red arrow) points backward from C3 to C2 — the tip pointer moves back and C3 is dropped. checkout -- foo.py (yellow box) sits off to the side labelled "history UNTOUCHED, discards file edits only" — it never touches the graph at all. One picture, three utterly different "undos": forward, backward, and sideways.


Recall

Recall After

git init on an empty folder, how many commits exist — and how can you make one with no file changes? Zero — init creates the .git folder and a branch pointer (main or master) aimed at nothing. ::: You need one git add + git commit for your first snapshot, or git commit --allow-empty to record a deliberately empty commit.

Recall Fast-forward vs three-way merge — the deciding question, and how do you force a merge commit anyway?

"Did both branches gain commits since they split?" ::: If only one did → fast-forward (no merge commit); pass --no-ff to force a merge commit anyway. If both did → three-way merge (a merge commit with two parents).

Recall When

git merge hits a conflict, does it create the merge commit automatically? No — it stops, writes conflict markers into the file, and waits. ::: You edit the file to one final version, remove all markers, git add it, then git commit — only then is the merge commit born.

Recall When does

git push get rejected, and when does it succeed instantly? Rejected when you diverged (remote is ahead) — you must pull first. ::: Succeeds instantly when the remote (origin/main) is your ancestor (remote behind) — the server just fast-forwards.

Recall Which undo is safe on already-pushed commits?

git revert — it adds a new inverse commit instead of rewriting history. ::: git reset --hard rewrites the tip and breaks collaborators.

Recall What is

HEAD? A pointer that answers "where am I in the history right now?" — normally it points at a branch, which points at your latest commit. ::: In detached-HEAD state it points straight at a commit with no branch attached.


Where to go next