4.5.9 · D1Software Engineering

Foundations — Git workflows — Gitflow, trunk-based development

2,183 words10 min readBack to topic

Before you can understand "should this feature branch merge into develop or main?", you must first be able to picture what a commit, a branch, and a merge actually are. This page builds every one of those pictures from absolute zero, in the order the parent topic secretly depends on them.


1. A commit — the atom of everything

The picture. Imagine a photo of your whole folder. Take an edit, snap another photo. Each photo remembers "the photo I came after." String these photos together and you get a chain running left-to-right in time.

Figure — Git workflows — Gitflow, trunk-based development

Why the topic needs it. Every branch name, every merge, every tag in the parent note ultimately points at one of these snapshots. If you don't see commits as beads on a string, phrases like "main is where every commit is a tagged release" are meaningless. The whole workflow is a story about which beads connect to which.

The little code a1f3c under each bead is its commit hash — a unique fingerprint. You never invent it; Git computes it. Think of it as the serial number stamped on that photo.


2. A branch — a movable name-tag

The picture. Two sticky notes — main and feature/login — can sit on the same commit at first. As feature/login gets new commits, only that sticky note slides right; main stays put. Now the two names point at different beads. That is the entire mechanism of "branching."

Figure — Git workflows — Gitflow, trunk-based development

Why the topic needs it. Gitflow's main, develop, feature/*, release/*, hotfix/* are five sticky notes with naming rules. Trunk-based "one shared branch" means one main sticky note everyone slides forward together. Once you see branches as cheap movable tags (not copies), you understand why creating and deleting them is a trivial, everyday act — which is exactly what makes short-lived branches practical.

  • The command git checkout -b feature/login in the parent note ::: creates a new sticky note named feature/login on the current commit and moves HEAD onto it.

3. Diverging — why branches "drift apart"

The parent note keeps warning about branches that "drift" and pile up "integration debt." Here is the picture behind that word.

Figure — Git workflows — Gitflow, trunk-based development

The picture. Start at a shared commit C2. You add C3, C4 on your feature branch. Meanwhile teammates add M3, M4 on main. Now neither branch is "ahead of" or "behind" the other — they both moved. The two forks of the road are what a merge must later rejoin.

Why the topic needs it. The parent's central formula lives here. Every extra commit that lands on main while your branch is alive is one more fork-tine that must be reconciled. That is literally the ("changes/day × days alive") from the parent — see §7. Divergence is the geometric meaning of integration debt.


4. Merge — rejoining two forks

The picture. Below the fork from §3, a single new bead M5 reaches back with two arrows — one to your C4, one to main's M4. Everyone downstream of M5 now has both sets of changes.

Why the topic needs it. The parent's git merge --no-ff feature/login is explained only if you can see the difference between "slide the tag" and "tie a documented knot." The --no-ff merge commit is the "documentation anchor" the parent mentions — the knot that says this bundle of beads was the login feature.

  • Why does a fast-forward "erase" the feature boundary? ::: because it just moves the tag along the existing beads — no knot commit is created, so nothing records that those beads formed one unit.

5. Merge conflict — when the forks touched the same line

The picture. If your fork changed line 10 of checkout.py and main's fork also changed line 10, the merge cannot proceed automatically. If the two forks touched different lines, Git merges them silently.

Why the topic needs it. This is the event whose probability the parent formula predicts. The word "overlap" in the parent (probability per change of "overlapping your edits") means exactly: did the other change touch a line you also touched?


6. Tag — a permanent nameplate on one commit

Why the topic needs it. "Every commit on main is a tagged release" and "releases are cut from trunk using tags" both rely on this. A tag is how a team says "this exact snapshot is what we shipped as version 1.2.1." The version numbers themselves follow Semantic Versioning.


7. The symbols in the conflict formula

The parent drops four symbols into its probability model. Here is each, from zero.

Symbol Plain words The picture
"lambda" — how many conflict-risky changes land on the shared branch per day forks sprouting on the road, so many per day
how many days your branch stays alive before merging the length of your fork before the knot
(rate) × (days) = total diverging changes you'll face count of all forks that appeared while you were away
probability one such change touches a line you also touched one fork randomly crossing your lane

Why each tool is the right one:

  • Why multiply ? Because "things per day" times "number of days" gives "total things" — the same reasoning as "3 apples/day for 4 days = 12 apples." This is why the parent writes .
  • Why the exponent, , and not multiplication? Because we need every one of the changes to miss your lines. "This one misses AND that one misses AND …" for independent events means multiplying the miss-probability by itself once per change — and repeated multiplication is an exponent. That is precisely why exponentiation, not addition, is the correct tool here.
  • Why ? If is the chance a change hits you, then is the chance it misses. We build the clean-merge story out of misses.
Figure — Git workflows — Gitflow, trunk-based development

Reading the curve. At (merge instantly) the exponent is , and anything to the power is , so clean, meaning . As grows the clean probability decays toward , so climbs toward . This falling exponential is the entire mathematical argument for "merge early, merge often."


8. The two enabling technologies (named, so you can look deeper)

The parent says trunk-based only works because of two things. You don't need to master them yet, but you must know what each is:

  • Continuous Integration — a robot that runs the whole test suite on every push, so a broken shared branch is caught in minutes, not days. Deep-dive: Continuous Integration.
  • Feature flags — an if switch that lets half-finished code ship to production turned off, so you can merge daily without exposing users to it. Deep-dive: Feature Flags.

The small merges themselves happen through Branching and Merging and are reviewed via Pull Requests and Code Review. Whether the whole thing is working is judged by DORA Metrics.


Prerequisite map

Commit = snapshot + parent

Branch = movable name-tag

Divergence = two forks

Merge = knot with two parents

Merge conflict = same line touched

Tag = permanent nameplate

Conflict formula lambda T and p

Gitflow five branch types

Trunk-based one shared branch

Parent: Git Workflows

Read it top-down: commits give you beads, branches give you movable tags, tags-that-drift-apart give divergence, rejoining gives merge and its conflicts, and those combine into the formula — after which the two workflow shapes (Gitflow, trunk-based) finally make sense. All roads feed the parent topic.


Equipment checklist

Test yourself — reveal only after answering aloud.

  • A commit stores what two things? ::: a full snapshot of the project and a pointer to its parent commit.
  • Is a branch a copy of your code? ::: No — it is a movable name-tag (pointer) to a single commit.
  • What does HEAD mean? ::: "the branch I am currently on" — the you-are-here arrow.
  • Two branches have diverged when… ::: each has commits the other lacks, sharing a common ancestor.
  • A merge commit is special because it has… ::: two parents, tying two diverged lines together.
  • What does --no-ff force? ::: a knot (merge commit) even when a fast-forward slide was possible, preserving the feature boundary.
  • A merge conflict happens when… ::: both branches edited the same line differently and Git refuses to guess.
  • How does a tag differ from a branch? ::: a tag is pinned permanently to one commit; a branch slides forward.
  • In , what is ? ::: total number of diverging changes = rate per day × days alive.
  • Why an exponent and not a sum in the formula? ::: because every independent change must miss your lines (AND-of-independent-events = repeated multiplication = exponent).
  • What is when ? ::: , because (a guaranteed-clean merge).