Skip to content

# Git DAG Simulator

Build a commit graph with four git commands and watch what each one does to it. Every commit, every ref move and every parent edge is drawn as it happens.

A worked example, rendered when the site was built. No JavaScript required. It is the scenario called A true merge: Both branches moved, on different files, so the merge makes a commit with two parents. With JavaScript on, this is replaced by the same engine with a command builder attached, so you can run these commands yourself and start from any of the five scenarios.

## The commands

8 steps

Every command in the worked example, and what it did
Step | Command | Result | What happened
--- | --- | --- | ---
1 | git commit -m "Add the readme" (README.md) | ok | Commit 194fc6e records a change to README.md on main, and it is a root commit, so it has no parent.
2 | git commit -m "Expand the readme" (README.md) | ok | Commit 90031ec records a change to README.md on main, and its parent is 194fc6e.
3 | git branch feature | ok | feature now points at 90031ec. HEAD did not move, so you are still where you were.
4 | git checkout feature | ok | HEAD is attached to feature, which points at 90031ec.
5 | git commit -m "Write the feature" (src/main.rs) | ok | Commit 4f960b4 records a change to src/main.rs on feature, and its parent is 90031ec.
6 | git checkout main | ok | HEAD is attached to main, which points at 90031ec.
7 | git commit -m "Jot down the plan" (notes.txt) | ok | Commit 14098f1 records a change to notes.txt on main, and its parent is 90031ec.
8 | git merge feature | merge | A merge commit 9d3ba49 joins both histories. Its first parent is 14098f1, where HEAD was, and its second is 4f960b4, the tip of feature.

## The graph

## Refs

3 refs

Where every ref points
Ref | Points at | Commit
--- | --- | ---
HEAD | main | 9d3ba49
feature | 4f960b4 | 4f960b4
main | 9d3ba49 | 9d3ba49

## Commits

5 commits

The graph as a list, newest first
Commit | Refs here | Message | Parents | File
--- | --- | --- | --- | ---
9d3ba49 | HEAD, main | Merge branch 'feature' | 14098f1, 4f960b4 | none, a merge changes nothing itself
14098f1 | none | Jot down the plan | 90031ec | notes.txt
4f960b4 | feature | Write the feature | 90031ec | src/main.rs
90031ec | none | Expand the readme | 194fc6e | README.md
194fc6e | none | Add the readme | none, this is the root commit | README.md

The ids here are synthetic, drawn from a seeded generator rather than hashed. If you want the real function, the Hash Generator runs SHA-1 and its successors over your own input, and Hashprint turns a digest into a picture you can compare at a glance.

About this lab3 paragraphs

Four commands, and nothing else: commit, branch, checkout (including a detached `HEAD`) and merge, in both of its shapes. A fast-forward moves a ref along a chain that already exists and makes no commit; a true merge makes one commit with two parents, and the drawing shows both edges. Commands are built from pickers rather than typed at a fake shell prompt, so every argument is a real choice from the repository in front of you.

Nothing here is random and nothing reads a clock. Commit ids are synthetic: seven hex characters from the site's seeded generator, not a real SHA-1, because teaching the graph does not need the hash function and a seeded id makes the whole repository reproducible. Pick a scenario, replay its commands, and you get the same ids every time, on any machine.

What it does not do is the rest of git. `rebase -i`, `cherry-pick`, `reset`, `revert`, `stash`, `reflog` and anything to do with remotes are recorded as deferred, and a merge that collides is marked rather than resolved: the lab shows you that a conflict happened and on which file, and it stops there. There is no diff editor.