Theory

Updated

October 30, 2026

Theory: History is a Query over the DAG

git log is not a file. It is a walk of commit objects from one or more tips, filtered by paths, dates, pickaxe, and ranges. Blame is a walk of lines through that DAG.

Invariants

  1. Reachability: git log B shows commits reachable from B by following parents.
  2. Two-dot A..B = reachable from B, not from A (what a feature branch adds).
  3. Three-dot A...B = reachable from A or B, not from the merge base (symmetric difference). A pull-request diff is often git diff A...B (merge-base to B).
  4. git blame attributes each line of a file to the commit that last touched it; -C also hunts copies.
  5. Pickaxe -S/-G filters commits by whether a string’s count or a regex’s hunk changed.
 A ← B ← C ← G     main
      ↑
      D ← E        feat

 main..feat     =  D, E
 main...feat    =  G, D, E     (both sides since split)

Why this matters on a forge

A PR “files changed” list is this query, computed on the server. Reviewers who can run git log main..HEAD and git diff main...HEAD on Linux are not trapped in the UI.

What can break

  • Reading A..B as “between A and B inclusive on a line” (it is set difference of reachability).
  • git blame on a file that was reformatted in one giant commit (whitespace; -w).
  • --follow only works when you name one path.

How to read this chapter

Log flags, ranges, show, blame, pickaxe, shortlog, then a lab that finds a line’s origin in desk-api. The last page keeps blame useful across mass reformats with .git-blame-ignore-revs, and follows moved code with -C.