Topic branches, squash merges, one cherry-picked commit at a time. Our whole git flow, drawn out below. Click through it, then keep this page open on your second monitor while you work.
One finished task travels: topic branch → your personal branch → promote branch → main. Step through it and watch the graph draw itself.
based on a true story: this exact run shipped the page you are reading — issue #6 → PR #3 → PR #5 → main. one wrong merge button along the way, zero casualties. (every link is the real thing.)
Your personal branch collects everything: finished work, half-finished experiments, old syncs. Merging it into main would drag all of that along. The only thing that ever travels to main is a promote/… branch carrying one cherry-picked squashed commit. That's the whole trick.
Three moves that look like ceremony until you see what each one buys you.
Your topic branch is allowed to be messy — that's what it's for. The squash merge folds all of it into one tidy commit on your personal branch, with the PR title as its message. History stays readable, and promotion becomes possible: you can only cherry-pick "the work" if the work is one commit.
The promote branch starts from fresh main and receives exactly one commit — by SHA, so there is no guessing. It cannot drag along your personal branch, no matter how far it has drifted. Finished work travels; everything else stays home.
Branch protection on main means the merge button stays grey until CI passes and Robbin approves. So experiment freely: the repo's rules, not your nerves, are what keep main safe. You genuinely cannot break it from a branch.
Customer feedback lands as a GitHub issue, and the issue number becomes
the name of the work everywhere else. Here's the loop — starring a made-up issue
#47 — and the exact moments where things get linked.
topic/47-export-crash. GitHub doesn't parse branch names; the number is
for humans, and it's enough.#47 adds a little cross-reference to the issue's timeline
— nice, optional.Fixes #47 in the description — this is the
linking moment. The PR and the issue now point at each other under
Development on both pages.Fixes #47 in the squash message. The
keyword rides the cherry-pick all the way to the promote PR…main, the issue closes
itself. Open while the fix only exists in your personal branch, closed exactly
when customers actually have it.forgot the magic words? link the PR from either Development sidebar afterwards, or close the issue by hand with a comment. nothing is ever lost. true story: this page was issue #6 — it closed itself the second PR #5 hit main.
Issues and pull requests share a single number
sequence per repository — a PR is secretly a special kind of issue.
That's how feedback can be issue #2 while the next PR becomes
#3. A bare #47 in any text links to whatever owns that
number, so it can never point at the wrong thing.
#47 on its own only links.
Fixes #47, Closes #47, or Resolves #47
auto-closes the issue — but only when
the commit reaches main. On your personal branch nothing fires, and that's
correct: the feedback isn't done until it ships.
The issue page's Create a branch button links branch and issue instantly, but it branches off main by default. If you use it, click Change branch source and pick your personal branch, then fetch in GitHub Desktop. Branching locally with the number in the name is the simpler habit.
The whole loop as commands, for when you know what to do and just need the how. Desktop / VS Code folks: the stepper above is your cheat sheet.
pink bits = fill in your own; yellow = your personal branch (change your name up top)
| branch | pattern | example |
|---|---|---|
| main | just main | main |
| personal branch | your first name | lars |
| topic | topic/task-in-kebab-case | topic/git-workflow-guide |
| promote | promote/ + same task name | promote/git-workflow-guide |
short, lowercase, dashes-not-spaces. "topic/stuff" tells nobody anything. working from an issue? number first: topic/47-export-crash.
still lost? ping Robbin. really — that's the mentor deal.