skip to content

questions

20

In Git, which branches does the Git Flow model define and what is each one for?

level: juniorimportance: must knowfreq 62%

answer

  1. Count the branches that live forever
  2. Two permanent lines, three temporary types
  3. One branch integrates, one only records releases
  4. Hotfixes start where production is
  5. Releases and hotfixes land in two places

basics

~20 s

Git Flow defines two permanent branches, main and develop, plus three temporary types: feature branches cut from develop, release branches cut from develop, and hotfix branches cut from main. Releases and hotfixes merge back into both permanent branches.

solid answer

~40 s

Git Flow prescribes five branch roles over ordinary Git branches. **main** holds only released code and is normally tagged at each release; nobody commits to it directly. **develop** is the integration branch where finished work accumulates between releases. **Feature branches** start from `develop` and merge back into `develop`. **Release branches** start from `develop` when scope is frozen, take only stabilization commits, and merge into `main` *and* back into `develop`. **Hotfix branches** start from `main`, because they patch what is in production, and also merge into both. The double merge is the point people miss: `main` and `develop` are separate lines of history, so a fix landed on one is simply absent from the other until it is merged there too.

go deeper

for a junior

Be ready to name all five roles and say where each branch starts and ends. The two you must not mix up: features come off develop, hotfixes come off main.

for a middle

Explain why the hotfix and release merges go into both permanent branches, and what divergence between main and develop means in three-way-merge terms.

for a senior

Expect to justify the model against a team's release cadence, and to describe the conflict cost that accumulates while develop runs ahead of main between releases.

for a principal

Own the argument for when the ceremony pays for itself: several supported versions in the field justify the extra branch set, a single continuously deployed service does not.

## What Git Flow actually is Git Flow is a branching model published by Vincent Driessen in 2010. Git itself knows nothing about it: every branch below is an ordinary Git branch — a movable pointer to a commit — and the model is a naming convention plus rules about where a branch starts and where it merges back. There is an optional third-party helper script invoked as `git flow`, distributed separately from Git, but the whole model works with nothing but `git branch`, `git merge` and `git tag`. ## The two permanent branches **main** (called `master` in the original article) contains only code that has been released. Each commit on it corresponds to a shipped version and is normally marked with an annotated tag. No one commits to `main` directly; commits arrive only by merging a release branch or a hotfix branch. **develop** is the integration branch. Finished features accumulate there between releases, so its tip means "the next release, so far". Day-to-day work in a Git Flow repository starts from `develop`, not from `main`. Because both branches live forever, they permanently diverge and re-converge. `git log main..develop` almost always lists commits — that is the model working as designed, not a problem to fix. ## The three supporting branch types **Feature branches** start from `develop` and merge back into `develop`, conventionally named `feature/<something>`. Work on a feature branch never touches `main` directly. **Release branches** are cut from `develop` once the team fixes the scope of the next version, conventionally `release/1.4.0`. Only stabilization commits belong there — bug fixes, version bumps, changelog edits — while new features keep landing on `develop` in parallel. When the version ships, the release branch merges into `main` (which is then tagged) and back into `develop`, and is deleted. **Hotfix branches** start from `main`, not from `develop`, because they fix what is running in production right now, and unreleased work on `develop` must not be dragged along. Conventionally `hotfix/1.4.1`. They merge into `main`, which is tagged again, and into `develop`. ## Why releases and hotfixes merge in two directions This double merge is the structural heart of the model. `main` and `develop` are different lines of history; a commit merged into one is not present in the other. If a hotfix landed only on `main`, the next release branch — cut from `develop` — would not contain the fix, and shipping it would reintroduce the very bug that was just patched in production. Merging the hotfix into `develop` as well makes the fix an ancestor of both lines, so later merges treat it as already integrated instead of as a conflicting change. The same argument applies to a release branch: stabilization commits made on it must travel back to `develop`, or they are lost at the next release. ## What Git does at each step Every step is a plain three-way merge, using the commit where the branch diverged as the merge base. Git Flow conventionally uses `git merge --no-ff`, which records a merge commit even when the target could simply fast-forward. The effect is that each feature shows up in history as one merge commit with its own commits on a side branch, so `git log --first-parent develop` reads as a list of features rather than a flat stream of individual commits. Deleting a feature or release branch after the merge loses nothing. The commits remain reachable through the merge commit; only the pointer disappears. ## Where the model fits Git Flow was designed for software with explicit, versioned releases — desktop applications, libraries, on-premises products, anything where several versions exist in the field at once. The release branch gives a place to stabilize while feature work continues; the hotfix branch gives a way to patch what shipped without shipping anything unreleased. It fits a service deployed many times a day badly: `develop` is then never meaningfully ahead of `main`, and every change pays for a second merge and a branch that exists for minutes. Driessen later added a reflection note to the original article steering continuously delivered web applications toward simpler, single-main workflows. ## The vocabulary trap Being able to say that `git flow` (the helper script) and Git Flow (the model) are different things — and that Git ships neither — is what separates someone who understands the model from someone who has memorized a tool's menu.

  • Why does Git Flow prescribe `git merge --no-ff` when merging a feature branch into develop?
    Without it, Git fast-forwards when `develop` has not moved, and the feature's commits blend into a flat line with no record that they belonged together. `--no-ff` forces a merge commit, so `git log --first-parent develop` reads as one entry per feature and `git revert -m 1` on that merge can back the whole feature out.
  • What breaks if a hotfix is merged into main but never into develop?
    The fix exists only on the released line. The next release branch is cut from `develop`, which does not contain it, so the shipped version reintroduces the bug. Worse, when that release later merges into `main`, Git sees two histories that changed the same lines differently and you get a conflict at the least convenient moment.
  • Does deleting a release branch after merging lose the stabilization commits?
    No. Those commits are reachable as parents of the merge commits on `main` and `develop`, so they stay in history and in `git log`. A branch name is only a pointer; deleting it removes the label, not the objects. Only commits that no ref can reach eventually become candidates for garbage collection.

saying these in an interview costs you the question

  • Says Git Flow defines only main and develop
  • Thinks feature branches are cut from main
  • Claims a hotfix only needs merging into main
  • Calls release branches permanent, long-lived branches
  • Believes git ships a built-in flow subcommand

context

open as a page

Why doesn't a new Git tag like v1.4.0 reach the remote after git push, and how do you send it?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A plain git push sends branch refs only; tags live in refs/tags and are excluded by the default refspec. Push one explicitly with git push origin v1.4.0, or push a branch plus its reachable annotated tags with git push --follow-tags.

open as a page

What is trunk-based development, and how long do its branches live in Git?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Trunk-based development means everyone integrates into one shared branch — the trunk, usually main — continuously. Branches off it are short-lived, typically merged back within a day or two, so the repository never accumulates long-running parallel lines of development.

open as a page

In Git, what does git format-patch produce, and how does a maintainer apply it?

level: middleimportance: must knowfreq 45%

basics

~20 s

git format-patch writes one mbox-formatted file per commit, containing the message plus the diff and author metadata. A maintainer replays them with git am, which recreates the commits with their original author, date and message rather than just applying a diff.

open as a page

How do you split a large Git change into a series of commits a maintainer can review?

level: middleimportance: must knowfreq 55%

basics

~20 s

Make each commit one self-contained step that builds and passes tests on its own: preparation and refactoring first, behaviour change second, then tests and docs. Order them so a reviewer reading top to bottom sees the change being argued, not assembled.

open as a page

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

level: middleimportance: must knowfreq 58%

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.

open as a page

For marking a release in Git, what does an annotated tag give you that a lightweight tag does not?

level: middleimportance: must knowfreq 60%

basics

~20 s

An annotated tag creates a real tag object storing a tagger name, email, date, message and optional signature, and can be verified. A lightweight tag is just a ref file pointing at a commit, carrying no metadata of its own.

open as a page

How do you get a hotfix committed on a release branch back into main, and what breaks if you don't?

level: middleimportance: must knowfreq 70%

basics

~20 s

Propagate it deliberately: merge the release branch into main, or replay the commit with git cherry-pick -x. Skip it and the next release cut from main ships without the fix, so the bug returns as a regression nobody expects.

open as a page

In Git, what does the -s flag on git commit add, and why do projects require it?

level: juniorimportance: should knowfreq 42%

basics

~20 s

The -s flag appends a Signed-off-by trailer carrying your configured name and email. Projects governed by the Developer Certificate of Origin read that line as your certification that you have the right to submit the code under the project's licence.

open as a page

In GitLab Flow, what are environment branches and how do changes reach production?

level: middleimportance: should knowfreq 40%

basics

~20 s

Environment branches are long-lived Git branches named for deploy targets, such as staging and production, whose tips represent what each environment runs. Changes always land on main first and are then merged downstream, never committed directly to an environment branch.

open as a page

What does git describe output like v1.4.0-14-g2414721 mean, and what does it need to work?

level: middleimportance: should knowfreq 40%

basics

~20 s

It names the nearest reachable tag (v1.4.0), the number of commits since it (14), and the abbreviated commit id prefixed by g for git (2414721). By default it searches annotated tags only, so a repository without one cannot be described.

open as a page

In trunk-based development, how do you merge unfinished work into main safely?

level: middleimportance: should knowfreq 45%

basics

~20 s

You make the incomplete code inert rather than keeping it on a branch: merge it switched off behind a flag, or introduce it behind an abstraction that still routes to the old implementation. The trunk stays releasable because the new path is never taken.

open as a page

As a maintainer, how do you fetch and merge a contribution from someone's fork using Git alone?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Fetch directly from the contributor's repository URL and branch, which lands in FETCH_HEAD, review and test it on a local topic branch, then merge it into your integration branch — typically with a no-fast-forward merge so the contribution stays visible as a unit.

open as a page

A maintainer says your emailed Git patch series no longer applies — how do you resend it?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Rebase your topic branch onto the current upstream tip, re-run the tests, then regenerate the series with git format-patch using a bumped version prefix and a range-diff against the previous round, and reply in the same mail thread.

open as a page

Which Git branching model fits a team deploying to production several times a day, and why?

level: seniorimportance: should knowfreq 48%

basics

~20 s

A single long-lived main with short topic branches, GitHub Flow style. At that cadence a develop branch is never meaningfully ahead of main, so it adds a second merge and a second place for fixes to be missing without buying any stabilization window.

open as a page

With release/1.4, release/1.5 and main all supported, how should a security fix reach all three?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Commit the fix on the oldest affected line, release/1.4, then merge forward: 1.4 into 1.5, 1.5 into main. Each merge makes the fix reachable, so git branch --contains proves coverage instead of you having to trust three independent cherry-picks.

open as a page

Why can a branch that passed CI still break main when merged, and what limits that?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Because the branch was verified against its own commits on an older base, not against the merged result. Changes that landed on main since the branch's merge base are never combined with it until the merge happens, so a clean textual merge can still be logically broken.

open as a page

How would you migrate a repository from Git Flow to a single long-lived main branch?

level: principalimportance: should knowfreq 30%

basics

~20 s

Converge the two lines first: ship or merge everything on develop into main so nothing is stranded, re-point in-flight branches onto main, then delete develop locally and on the remote and update every ref, hook and upstream that named it.

open as a page

How many long-lived maintenance branches should a project keep, and how do you bound the backport cost?

level: principalimportance: should knowfreq 32%

basics

~20 s

As few as the support promise requires — each extra live line multiplies every fix by another merge or cherry-pick, retest and tag. Bound the cost with a published end-of-life date, strictly ordered merge-up, and no refactoring on maintenance lines.

open as a page

What must a team have in place before moving from long-lived branches to trunk-based development?

level: principalimportance: should knowfreq 30%

basics

~20 s

Automated verification the team actually trusts on every merge, changes that can be split into small independently-shippable pieces, a way to keep unfinished work inert on the trunk, and a release model that does not require several shipped versions to be maintained in parallel.

open as a page