skip to content

In Git, how does GitHub Flow's single-main topology differ from Git Flow's develop branch?

level: middleimportance: must knowfreq 58%

answer

  1. Count the long-lived branches in each
  2. How many merges before a change reaches main
  3. Ask what merge-base against main returns
  4. One log lists changes, the other lists releases
  5. A fix on two lines needs two merges

basics

~20 s

GitHub Flow keeps one long-lived branch: topic branches start from main and merge straight back, so a change integrates once. Git Flow adds develop as a second integration point, so work merges at least twice before it reaches main.

solid answer

~40 s

In GitHub Flow, `main` is the only long-lived branch. Every unit of work is a short-lived topic branch created from the current tip of `main` and merged back into `main`, then deleted — one integration point, one merge base, and `main` is expected to be releasable after each merge. Git Flow inserts `develop` between the work and `main`: a feature merges into `develop`, and only a release or hotfix branch merges into `main`. That second integration point changes concrete Git facts. The merge base between a feature branch and `main` is the previous release merge, not a recent commit. `git log --first-parent main` reads as a list of releases rather than a list of changes. And any fix needed on both lines has to be merged twice, because `main` and `develop` are separate histories.

code

bash · 15 lines
bash
# GitHub Flow: one integration point
git switch main && git pull
git switch -c fix-login-timeout
# ...commits...
git switch main
git merge --no-ff fix-login-timeout   # main is releasable again

# Git Flow: the same change crosses two merges
git switch develop
git switch -c feature/login-timeout
# ...commits...
git switch develop && git merge --no-ff feature/login-timeout
git switch -c release/2.1 develop
git switch main && git merge --no-ff release/2.1
git tag -a v2.1 -m "2.1"

go deeper

for a junior

Recall the branch counts: GitHub Flow has one long-lived branch, Git Flow has two. Know that features merge into develop under Git Flow and into main under GitHub Flow.

for a middle

Explain the consequences in Git terms — what merge-base returns, how many merges a change crosses, and why a fix must be applied to both permanent branches under Git Flow.

for a senior

Be able to say where conflict work lands under each model — continuously in topic branches, or concentrated in the release branch — and who ends up doing it.

for a principal

Frame the choice as buying structure you may not need: a second permanent branch is only worth its double merges when releases are discrete events rather than a continuous stream.

## Two conventions over the same primitive GitHub Flow and Git Flow are both conventions layered on ordinary Git branches. Neither is a Git feature. What distinguishes them is how many long-lived branches exist and, as a direct consequence, how many merges a change crosses before it is considered shipped. ## GitHub Flow in Git terms One permanent branch, `main`. A unit of work becomes a descriptively named topic branch created from the current tip of `main`, is pushed to the remote so others can see it, and is merged back into `main` when it is finished; the branch is then deleted. The rule that gives the model its shape is that `main` is expected to be releasable at all times, so merging into `main` *is* the moment a change is "in". The review conversation around that merge happens on the hosting platform and is not a Git mechanism — the Git-side story is just: branch from `main`, merge to `main`. ## Git Flow's second integration point Git Flow adds `develop`. Feature branches start from `develop` and merge back into `develop`. `main` receives commits only through release branches and hotfix branches. A single change therefore crosses at least two merges — into `develop`, then into `main` via a release branch — and may sit on `develop` for days or weeks in between. ## What the merge base tells you The merge base is the commit where two branches last shared history, and it is what a three-way merge diffs against. In GitHub Flow, `git merge-base my-topic main` returns a recent commit, because the topic was cut from `main` days or hours ago. In Git Flow, `git merge-base feature/x main` returns the last point where `develop` and `main` converged — the previous release merge, which may be weeks old. That single fact explains most of the practical friction. Cross-line operations in Git Flow (taking a hotfix from `main` onto `develop`, or checking whether production contains a given change) work across a wide divergence, so they conflict more and need explicit merges. In a single-main model the two things you are comparing are usually a handful of commits apart. ## The shape of main's history `git log --first-parent main` walks only the first parent of each merge, which is the branch you were on when merging. Under GitHub Flow that produces roughly one entry per merged change — a change-level changelog. Under Git Flow it produces one entry per release or hotfix, because those are the only merges `main` ever receives. Neither is wrong. Git Flow's release granularity is deliberate: `main` answers "what versions exist", and `develop` answers "what changed". A single-main repository answers both with the same log and marks versions with tags instead of with branch structure. ## Propagating a fix GitHub Flow: the fix merges into `main` and it is done, because there is no other line for it to be missing from. Git Flow: a production fix goes on a hotfix branch cut from `main`, and must then also be merged into `develop`. Skip that and the next release, built from `develop`, ships without the fix. This is the single most common Git Flow operational mistake, and it is a direct consequence of having two permanent branches. ## Refresh pressure and where conflicts land In a single-main repository, `origin/main` moves with every merged change, so topic branches age fast; teams keep them short and bring `main` back into the branch often. Conflicts are therefore small, frequent, and resolved by whoever wrote the change. In Git Flow, `develop` moves fast while `main` moves rarely. The divergence between them is not resolved continuously — it is absorbed at release time, in the release branch, often by whoever is running the release rather than by the author. The total conflict work is not obviously higher; it is concentrated differently, and later. ## Marking releases GitHub Flow has no branch that means "this is version 2.1"; an annotated tag on a commit of `main` does that job. Git Flow encodes the same information structurally, in the release branch and its merge. If you need to support several released versions simultaneously, structure buys you something a tag does not: a place to keep committing. ## What an interviewer is checking That you can talk about these models as *branch topology* rather than as a list of ceremonies — that you can say what `git merge-base` returns, how many merges a change crosses, and what `main`'s log looks like under each.

  • Under GitHub Flow, what marks the fact that a particular commit on main was released as version 2.1?
    An annotated tag, created with `git tag -a v2.1`, pointing at that commit. There is no branch structure encoding the release, so the tag carries the whole claim: it names the version, records who made it and when, and `git describe` can then express any later commit relative to it.
  • If a single-main team suddenly needs to patch a version released three months ago, what does the topology cost them?
    They have no branch sitting at that point, so they create one from the release tag and fix there. That is fine once. The signal to watch is frequency: if several old versions need patching concurrently, you are running a multi-version product and the branch-per-release structure that Git Flow provides starts paying for itself.
  • Why does `git log --first-parent main` look so different under the two models?
    `--first-parent` follows only the branch you were on when each merge was recorded. Under a single-main model every merged change is a first-parent entry on main, so the log lists changes. Under Git Flow only release and hotfix merges ever touch main, so the same command lists versions and the per-change detail lives on develop.

saying these in an interview costs you the question

  • Says GitHub Flow forbids merge commits entirely
  • Thinks develop is required for any team review
  • Claims both models have the same merge base to main
  • Believes main in Git Flow receives feature merges
  • Says a second integration branch removes conflicts

context