TT Lab
Get started
Learn Learning paths Courses

Git in Practice

There Is Only One Question That Picks a Strategy

Continue in TT Lab

One-line summary

The question to ask when choosing a branching strategy is not "which diagram looks nicest" but "when an outage happens, how do we plan to find the culprit commit in this repository?"

Why this is needed

Git Flow, GitHub Flow and Trunk-Based were each designed on the assumption of a different release cadence. If you ignore the assumption and take only the name, you get friction every day.

Strategy Branch kinds Merge frequency Where it fits
Git Flow 5 or more kinds 1–2 per week Scheduled releases, mobile apps, packages
GitHub Flow 2 kinds 1–3 per day SaaS that practices CD
Trunk-Based 1–2 kinds 5–10 or more per day Branch lifetime within 1 day, Feature Flags required

Trunk-Based demands a very high level of CI. If you start without Feature Flags, unfinished code is deployed as it is. Conversely, in a GitOps environment, Git Flow's release branches clash heavily with the automatic sync pattern. So if you use GitOps, GitHub Flow or Trunk-Based is recommended.

One common rule works anywhere. A feature branch that lives for 3 days or more is a breeding ground for merge conflicts.

How it works

There are three merge methods, and each leaves something different behind.

A cherry-pick is not cutting and pasting a stored diff. Git does not store diffs in the first place; it stores only snapshots. So it performs a three-way merge using the parent tree of the target commit as the common ancestor. This is why a cherry-pick can conflict.

What it looks like in the field

A hotfix is made on the release branch and not applied to main, so the same bug comes back to life in the next release — every team experiences this once. If you run release branches, "a hotfix must always be brought into main too" must be written as a policy sentence.

The same goes for tags. A lightweight tag is just a pointer, so nothing remains about who attached it, when or why. A release tag should be annotated with -a so that it can be used in an investigation later.

Keep the history for investigation

The value of a branching strategy shows not in ordinary times but when an incident occurs. In a situation like "it worked until yesterday but not today", the standard tool for finding the culprit commit is binary search. Git does this semi-automatically with git bisect. If you tell it a good commit and a broken commit, it checks out the midpoint, and if a person only answers good/bad, it halves the range each time. Ten steps are enough to find the cause among 1,000 commits.

For binary search to work well, the history must satisfy two conditions. Every commit must build and work, and commits must be small. If commits that do not work are mixed in, a person cannot answer good/bad and has to skip that point, and if a squash merge has turned each PR into a single commit, the search stops in front of that PR. That means you find out only "somewhere in this PR" and have to read the rest by hand. This is the passage behind the earlier statement that squash merges fix the bisection resolution at the PR size.

The choice after finding the culprit commit should also be decided in advance. There are two ways to undo it, and their natures are completely different.

When reverting a merge commit, one more thing is needed. Since it has two parents, you must specify which line to keep, as in -m 1, and usually parent 1 is the side that received the merge (main). And if you merge the same branch again after reverting a merge, no changes come in. From Git's point of view they are commits that have already been merged. It is better not to learn for the first time, in an urgent situation, that to bring the reverted changes back you have to revert the revert commit once more.

What you will do in the next lab

You will create a repository from scratch, merge a feature branch with --no-ff to leave an integration record, raise small fixes by fast-forward, attach a release tag, bring the hotfix from the release branch into main with a cherry-pick, and then clean up only the merged branches. At the end, you count the actual numbers in this repository and write a strategy comparison table.