EduFlow
Git & GitHub
0%

Section 1 · Git's Model

Reading History: Commits as a Graph

16 min3 min readChoose wisely

Lesson at a glance

Read a repository’s history with git log, git show and git diff, name any commit relative to HEAD, and picture history as a graph of snapshots linked to their parents.

  1. 1The everyday view
  2. 2Commits form a graph
  3. 3Naming commits
  4. 4Looking inside commits
  5. 5A real investigation

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Your portfolio has a handful of commits now. Six months from now it will have hundreds, and the question you’ll actually ask is never “what’s the history?” but something sharper: when did the footer break, what did this file look like before the redesign, which commit added that colour. Reading history well is how you answer those in seconds.

The everyday view

Plain git log prints every commit, newest first, with full IDs and dates. It’s thorough and tiring. This is the version to remember:

Terminal
git log --oneline --graph --all
Text
*   e5d81c0 (HEAD -> main) Merge branch 'dark-mode'
|\
| * d4a7f20 (dark-mode) Add dark mode colors
* | c39be11 Add projects page
|/
* b2e04f3 Add navigation bar with base styles
* a1c9d72 Add home page

Read it bottom to top for chronology. Each * is a commit; the lines show which commit came from which. In parentheses are labels: main and dark-mode are branches, and HEAD -> main means you’re on main. --all matters: without it, Git shows only commits reachable from where you are, and the dark-mode line would still be there only because it was merged.

Long output opens in a pager. Use the arrow keys or space to scroll and q to quit.

Useful filters, all combinable: -n 5 (last five), --stat (which files changed), -p (full diffs), --author="Maya", --since="2 weeks ago", and -- index.html (only commits that touched that file).

Commits form a graph

The output above is a drawing of a directed acyclic graph, or DAG. Directed, because each commit points to its parent. Acyclic, because you can’t follow parents and end up where you started; a commit can’t be its own ancestor. Most commits have one parent. The root commit has none. A merge commit has two.

Commits a1c9 and b2e0 in a row, then the line splits: c39b on the main line and d4a7 on the dark-mode line. Both lead to merge commit e5d8, which has two parents. The main label and HEAD point to e5d8; the dark-mode label points to d4a7. Arrows point from each commit to its parent. a1c9 b2e0 c39b d4a7 e5d8 main HEAD dark-mode
The same history as a commit graph

Keep this picture in your head. In section 2 you’ll see that a branch is nothing more than one of those labels, and every branching command moves a label or adds a circle.

Naming commits

You can name any commit by its ID, and Git accepts the shortest unambiguous prefix, usually 7 characters. IDs are hashes of the commit’s content, metadata and parent, so they never change, and in practice two different commits never share one.

Counting from where you are is often easier:

  • HEAD is the commit you’re on.
  • HEAD~1 (or HEAD~) is its parent, HEAD~3 is three generations back, following the first parent each time.
  • HEAD^2 is the second parent of a merge commit; on e5d81c0 above, that’s d4a7f20.
  • A branch name works anywhere a commit does: dark-mode~1 is b2e04f3.

Looking inside commits

Terminal
git show c39be11              # message, author, date and the diff it introduced
git show HEAD~2:styles.css    # the whole file as it was in that snapshot
git diff a1c9d72 HEAD         # everything that changed between two snapshots
git diff HEAD~3 HEAD -- styles.css   # the same, for one file only

git diff takes any two commits because each is a complete snapshot. The diff is calculated on demand by comparing them. The same goes for the diff git show prints: it’s this commit’s snapshot compared with its parent’s.

A real investigation

Here’s how these commands combine. A friend tells you the footer on your projects page has disappeared, and you’re sure it was there last week. Start by narrowing the history to the file that matters:

Terminal
git log --oneline --since="1 week ago" -- projects.html
Text
8f1e2aa Move project cards into a grid
c39be11 Add projects page

Two suspects. Look at the newer one in full:

Terminal
git show 8f1e2aa -- projects.html

The diff shows the <footer> block in red, removed along with the old list markup. That’s your culprit, found in under a minute. To grab the old footer markup without rewinding anything, print the file as it was in the parent commit and copy what you need:

Terminal
git show 8f1e2aa~1:projects.html

Nothing in this investigation changed a single file or commit. Reading history is always safe, so do it freely and often. Most “Git emergencies” I’ve seen were really reading problems: someone panicked before looking at the graph, and the answer was already sitting in git log.

You’ve seen labels like main and dark-mode sitting on commits. Section 2 starts with what those labels really are and why creating one costs almost nothing.

Key takeaways

  • History is a directed acyclic graph, each commit pointing to its parent, and a merge commit pointing to two.
  • git log --oneline --graph --all draws the graph in the terminal, including branches you’re not on.
  • git show <commit> prints one commit’s message and changes; git show <commit>:<path> prints a file as it was then.
  • HEAD~1 is the commit before the one you’re on, HEAD~3 three steps back along first parents.
  • git diff A B compares any two snapshots, and -- <path> limits it to one file.

Further reading

Check your understanding

4 questions

Question 1 of 4

git log on main doesn’t show a commit you made on your dark-mode branch. Why?

Question 2 of 4

You want to see styles.css exactly as it was two commits ago, without changing any files. Which command does that?

Question 3 of 4

How many parents does a merge commit that joins dark-mode into main have?

Question 4 of 4

Which command lists only the commits whose changes added or removed the text dark-mode?

Practice

Choose wisely

Read the graph

Here is the portfolio’s history as a commit graph, the same shape git log --oneline --graph --all draws in text. Arrows point from each commit to its parent.

:::figure The portfolio’s commit graph

Five commits. 1a2b and 3c4d in a row. From 3c4d, one line goes to 5e6f and another to 7a8b. Merge commit 9c0d has parents 5e6f and 7a8b. main and HEAD point to 9c0d; the label hero points to 7a8b. 1a2b 3c4d 5e6f 7a8b 9c0d main HEAD hero :::

You’re on main. Which commits does git log --oneline hero list?

Read the graph