Worked examples — Git workflows — Gitflow, trunk-based development
The scenario matrix
Think of a workflow as a machine with inputs (how long a branch lives, how busy the trunk is, whether the code is finished) and outputs (does it merge cleanly? where does the fix go? what version tag?). Every row below is a class of input the real world hands you. If we work one example per row, you never meet an unshown case.
| # | Case class | The "sign / limit" it probes | Example |
|---|---|---|---|
| A | Short-lived branch, quiet trunk | small , small → | Ex 1 |
| B | Long-lived branch, busy trunk | large , large → | Ex 2 |
| C | Degenerate: or | zero input → guaranteed clean | Ex 3 |
| D | Degenerate: or | max-collision and no-collision inputs | Ex 3 |
| E | Gitflow hotfix — where does the fix land? | routing across two homes | Ex 4 |
| F | Gitflow release stabilization | who fixes bugs, develop vs release/* |
Ex 5 |
| G | TBD with feature flag — deploy ≠ release | incomplete code shipped safely | Ex 6 |
| H | Real-world word problem | pick a number of daily integrations | Ex 7 |
| I | Exam twist — compare two strategies numerically | which branch policy wins? | Ex 8 |
Prerequisite ideas we lean on: Branching and Merging, Continuous Integration, Feature Flags, Pull Requests and Code Review, Semantic Versioning.
Figure 1 (below) is our map of this whole table. The horizontal axis is branch lifetime and the vertical axis is trunk busyness . The orange corner (bottom-left) is cells A and C — short branches on a quiet trunk, where risk collapses toward . The magenta corner (top-right) is cells B and D — long branches on a busy trunk, where risk climbs toward . The violet box is cell G: a feature flag lets you merge daily so you keep pulling yourself back into the safe corner even while building something large. Read the whole page as a journey between these two corners.

Example 1 — Cell A: short branch, quiet trunk
Step 1 — count the diverging changes. Why this step? depends on how many independent chances to collide accumulated while you were away. That count is : rate × time = expected number of arrivals. Half a day at 2/day averages exactly 1 change.
Step 2 — probability that the single change avoids you. Why this step? Each change independently misses your lines with probability . This is the "clean per-change" survival probability.
Step 3 — combine. Why this step? Independent events multiply, so all changes miss you with probability . The complement is the conflict chance.
Verify: With exactly one incoming change, the conflict chance must equal itself () — and it does. Units: = (changes/day)(days) = changes, dimensionless exponent. ✓
Example 2 — Cell B: long branch, busy trunk
Step 1 — count. diverging changes. Why? The branch drifted for 8 days on a busy trunk, so 40 independent collision chances (on average) piled up. This is exactly the "integration debt" the parent warned about.
Step 2 — clean survival. . Why? All 40 must miss you for a clean merge.
Step 3 — evaluate. Why? We raise the per-change survival to the 40th power; repeated multiplication by 0.9 shrinks fast.
Verify: Compare with Example 1: same , but went , and went . The exponential decay of is the whole argument for short branches. ✓
Figure 2 (below) plots exactly these two examples on one curve. The horizontal axis is branch lifetime ; the vertical axis is . The lower orange curve is the quiet trunk () and the upper magenta curve is the busy trunk (). The orange dot marks Example 1 () sitting low near the origin; the magenta dot marks Example 2 () pinned near the top. Notice how the curve bends upward fast then flattens as it presses against the dotted line at — that bending is the exponential decay of made visible.

Example 3 — Cells C & D: the degenerate inputs
Step 1 — . , so , hence . Why? Anything raised to the power is , so . Here is the plain reason why : the exponent counts how many incoming changes must miss you. If , there are no changes to worry about, so the product of "everyone missed you" is an empty product — a product with no factors — and an empty product is defined to be (just as an empty sum is ). A merge that happens the instant you branched cannot conflict. This is the limit trunk-based chases.
Step 2 — . , again for any . Why? If no conflict-causing change ever lands, branch lifetime is irrelevant — you could live 100 days safely. Same empty-product reasoning as Step 1: zero factors ⇒ . It confirms the danger is other people's changes, not time alone.
Step 3 — , with . , so . Why? If every single change touches your lines, then a single incoming change already guarantees collision. Because there is at least one factor of , so the product is genuinely (this is a non-empty product, unlike Steps 1–2). The formula correctly saturates at certainty.
Step 4 — , with . , so . Why? If no change ever touches your lines, then no amount of drift can hurt you — even 40 incoming changes all miss, and . This is the mirror image of Step 3: forces certainty of conflict, forces certainty of no conflict. Real life sits between the two.
Verify: All four are internal limits of the same formula — no special-casing needed. The three "safe" cases (, , ) each make via an empty product or a base of ; only with forces a certain conflict. ✓
Example 4 — Cell E: the Gitflow hotfix routing
Step 1 — branch off main. git checkout main && git checkout -b hotfix/1.2.1.
Why? Production is broken now. develop may hold half-finished features, so it is unsafe to build a fix on. main is the known-good live state.
Step 2 — merge into main, then tag.
git checkout main
git merge --no-ff hotfix/1.2.1
git tag v1.2.1Why the tag? Under Semantic Versioning, a backward-compatible bug fix bumps only the PATCH number: 1.2.0 → 1.2.1. --no-ff keeps a merge commit so history shows a distinct hotfix boundary.
Step 3 — merge into develop too.
git checkout develop && git merge --no-ff hotfix/1.2.1Why? "Hotfix Hits Both Homes." The next release is built from develop; if the fix is not there, the next release re-introduces the exact bug — a regression.
Verify: SemVer arithmetic — a PATCH bump takes MAJOR.MINOR.PATCH = 1.2.0 to 1.2.1, leaving MAJOR and MINOR unchanged. ✓
Example 5 — Cell F: stabilizing a Gitflow release
Step 1 — cut the release branch. git checkout develop && git checkout -b release/1.3.0.
Why? This branch is feature-frozen: only bug fixes allowed. It buffers the release so develop stays free for new work.
Step 2 — fix the two bugs on release/1.3.0.
Why? If you fix them on develop instead, (a) you cannot tell which fixes belong to this release, and (b) any unfinished feature freshly landing on develop would pollute the release. Stabilize on the frozen branch. This is the exact mistake the parent note flags.
Step 3 — start feature/reports off develop, in parallel.
Why? Because the release lives on its own branch, develop is unblocked — new feature work proceeds concurrently with stabilization. See Branching and Merging.
Step 4 — finish: merge release/1.3.0 into main (tag v1.3.0) and back into develop.
Why merge into develop too? So the two release bug fixes are not lost — same "both homes" logic as the hotfix.
Verify: Count the merge destinations: a release/* branch, like a hotfix/*, resolves into exactly 2 permanent branches. Both fixes are preserved via the develop merge — zero regressions. ✓
Example 6 — Cell G: trunk-based with a feature flag
Step 1 — strategy (a), the long branch. , so Why? 6 days of drift on a busy trunk = 30 accumulated collision chances → almost certain conflict. Since there is only one merge, this probability is the expected number of conflicting merges: .
Step 2 — strategy (b), per-merge probability. Each daily merge has : Why? Feature Flags let you merge incomplete code that stays off in production, so each day's branch lives only 1 day → far fewer accumulated changes per merge.
Step 3 — pick the right metric: expected number of conflicts. Why not just compare the single worst merge? A per-merge probability is not comparable across strategies with different merge counts — (a) has 1 merge, (b) has 6. The fair, apples-to-apples metric is the expected number of conflicting merges across the whole 6-day job. By linearity of expectation (add the per-merge probabilities):
Step 4 — interpret both metrics honestly.
- Expected count: vs . Strategy (b) actually has more conflict events.
- Cost per event: each (b) conflict spans only 5 changes and is caught same-day by Continuous Integration; the single (a) conflict spans 30 changes — the "gluing disaster."
Why does (b) still win? The 2.46 expected conflicts in (b) are tiny, fresh, and cheap to resolve; the ~0.96 expected conflict in (a) is a large, stale, release-threatening merge. Count of conflicts ≠ cost of conflicts — and integration debt is about cost.
Verify: so ; so per-merge ; . The metric to trust for total workload is the expected count; the metric to trust for danger is the size of the worst conflict. ✓
Example 7 — Cell H: real-world word problem
Step 1 — set the target. Require , i.e. Why? We invert the model: given the output (30% max risk) we solve for the input .
Step 2 — take logarithms. Because is trapped in an exponent, the log is the tool that pulls it down: Why ? turns "unknown in the exponent" into "unknown multiplied by a number" — the only clean way to isolate . This is exactly why the exponent being a real number (from the definition callout) matters: can take any real value, and works on the whole continuous curve.
Step 3 — solve for . Note , so dividing flips the inequality: Why flip? Dividing an inequality by a negative number reverses its direction — a standard algebra safeguard.
Verify: Plug back: , so — exactly the cap. The "≤ 1 day" trunk-based rule is quantitatively justified here. ✓
Example 8 — Cell I: exam-style twist
Step 1 — Team G: one merge. , so Expected conflicting merges . Why? Team G merges exactly once, so its expected number of conflicting merges is simply that single merge's probability — there is nothing to add.
Step 2 — Team T: per-merge probability. Each of the 20 merges has : Why per-merge count ? Rate 4/day over half a day = 2 accumulated changes, so each short branch faces only 2 collision chances.
Step 3 — Team T: expected total. By linearity of expectation (add the per-merge probabilities, no independence needed): Why linearity? The expected value of a sum equals the sum of expected values, regardless of dependence. Twenty merges each contributing give expected conflicting merges.
Step 4 — compare and interpret. vs . Why does "more events" still favour Team T? Cost per conflict is what hurts. Team T's conflicts span only 2 changes each — resolvable in minutes and caught same-day by Continuous Integration. Team G's single likely conflict spans 40 changes — a stale, release-threatening merge. Count of events ≠ cost of events; this mirrors DORA Metrics, where high deploy frequency with low change-failure rate beats rare, risky big-bang merges.
Verify: and ; . The exam trap is comparing raw event counts instead of per-conflict size. ✓
Active recall
Recall Predict-then-check the model's limits
Q: What is when ? ::: — no elapsed time means no incoming changes (empty product ⇒ ). Q: What is when ? ::: — no change ever touches your lines, so and there is nothing to collide with. Q: What is when and ? ::: — every change hits you, so a conflict is certain. Q: Why is allowed to be a non-integer? ::: It is an expected count of Poisson arrivals, and real powers extend to the whole continuous curve. Q: Two teams do the same work; one merges 20× more often. Who has smaller conflicts? ::: The frequent-merger — each merge accumulates far fewer changes.
Recall Gitflow routing
Q: A hotfix/1.2.1 off main merges into which branches, and what SemVer tag? ::: Into main and develop; tag v1.2.1 (PATCH bump).
Q: Where do release bugs get fixed — develop or release/*? ::: On the feature-frozen release/* branch, then merged back into both main and develop.