Skip to content

Draw squash-merged branches as merged in the Graph - #89

Merged
danipen merged 3 commits into
mainfrom
graph-squash-merged-branches
Sep 22, 2026
Merged

danipen merged 3 commits into
mainfrom
graph-squash-merged-branches

Conversation

@danipen

@danipen danipen commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Problem

Repos that land pull requests with squash (or rebase) merges — e.g. a merge queue configured to squash — leave no merge commit behind. The branch tip never becomes an ancestor of the default branch, so the Graph drew finished PRs as live, unmerged branches, next to real-merge PRs that did read as merged. Confusing.

Fix

Recover the landing by content instead of ancestry:

  • Renderer (pure, graph/squash.ts) — from the loaded window, find branch tips not reachable from the default branch (local or remote, whichever is newer) and each tip's merge base with it.
  • Main (git/read/squash-landings.ts) — a branch landed when a mainline commit newer than its merge base has the same patch-id --stable as the branch's whole base..tip diff (squash) or its tip commit (rebase merge). Three streamed patch-id passes (the diff-tree --stdin pipeline, generalised to also diff ranges via <tip> <base> lines) — never a spawn per branch.
  • Layout — a squash-landed tip counts as merged everywhere a merge source does (Hide merged, claim priority, structure-only, upstream comparison, packing lead-out) and gets a new squash edge into its landing commit.
  • Render — the squash edge is drawn exactly like a merge (solid connector, the landing wears the branch's merge ring): the branch IS merged. A dash was tried and rejected — dashes already mean WIP / guessed names / backport twins here, so it read as "pending".
  • Detail pane — the one place the missing ancestry is named: selecting a landing commit shows "Squash of <branch>", linking to that branch's changes view.

The hook only re-asks when the mainline tip or candidate set changes, and keeps the last answer while loading, so watcher refreshes don't re-run git or flicker.

Tests

  • Integration (squash-landings.test.ts, real git): multi-commit squash merge, rebase merge, never-landed branch, base cutoff.
  • Unit (squash.test.ts): candidate/merge-base derivation, remote-ahead default branch, synced branches, tags/HEAD/out-of-window skips.
  • Unit: squashedBranchesByLanding (landing → squashed branches).
  • Layout: squash edge + ring, Hide merged, upstream, structure-only, out-of-window landings.

Verified visually on a squash-merge-queue repo: all four squash-landed branches connect into their (#N) commit like merges; the landing's detail pane names the squashed branch and opens it (same files/stat as the squash commit).

🤖 Generated with Claude Code

Squash and rebase merges leave no merge commit, so a finished pull request's
tip never becomes an ancestor of the default branch. Recover the landing by
content: a mainline commit newer than the branch's merge base whose patch-id
equals the branch's whole base..tip diff (squash) or its tip commit (rebase).
Three streamed patch-id passes, never a spawn per branch.
A branch that landed by squash or rebase merge now reads as merged: a dashed
merge connector into its landing commit, the landing wears the branch's merge
ring, and it counts as merged everywhere a merge source does (Hide merged,
claim priority, structure-only, upstream comparison, packing lead-out).
A dashed connector read as pending or provisional — dashes already mean WIP,
guessed branch names and backport twins here — while the branch is plainly
merged. Draw the squash connector exactly like a merge instead, and name the
squash where an expert looks: the landing commit's detail pane says
"Squash of <branch>", linking to that branch's changes.
@danipen
danipen merged commit 97943bd into main Sep 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant