Branches, Fast-Forward and Three-Way Merges, and Branching Strategies: Concepts, Architecture, and Mental Model
Build a graph-first mental model of branch refs, HEAD movement, divergence, merge bases, fast-forward updates, true merge commits, conflicts, safe branch deletion, and practical branching strategies.
Learning objectives
- Explain local branches as movable refs and HEAD as the current branch attachment.
- Use reachability and merge bases to predict whether a merge can fast-forward.
- Distinguish fast-forward ref movement from a true two-parent merge commit.
- Explain the three conceptual inputs of a merge and why conflicts require human intent.
- Compare trunk-based, short-lived feature, release, and Git Flow-style policies as operational tradeoffs.
1. The practical problem — parallel work creates a graph, whether your policy admits it or not
Chapter 03 established that branches are refs pointing at commit objects. Chapter 05 taught you to interrogate commit reachability and parent relationships. The next operational question is what happens when two lines of work move independently and then need to become one deployable history.
Teams often learn branch commands as folder-like operations—“copy
main, work there, merge it back.” That metaphor breaks as soon as
histories diverge. A branch is a movable name. Switching branches
changes which commit HEAD is attached to and updates
the index and working tree accordingly. Merging is a graph operation
that either moves a ref forward or creates a new commit with
multiple parents.
2. Branch refs and HEAD movement
A local branch such as trunk is normally stored under
refs/heads/trunk. In ordinary branch work,
HEAD symbolically names that branch. A new commit
advances the current branch ref to the new commit; the commit does
not contain a branch name.
git branch --show-current
git symbolic-ref HEAD
git show-ref --heads
git rev-parse HEAD
These read-only commands separate the naming layer from the commit
graph. If trunk and feature/api both point
at the same commit, creating the second branch has not duplicated
any files or commits.
3. Divergence and reachability
Two branches diverge when each tip reaches commits the other tip cannot reach. Their most useful shared reference point is a merge base: a best common ancestor used as the base input for a three-way merge.
git merge-base trunk feature/api
git merge-base --is-ancestor trunk feature/api
git log --graph --decorate --oneline --all
The --is-ancestor form gives an especially direct
answer to “can this ref be fast-forwarded to that one?”: if the
current tip is an ancestor of the incoming tip, no divergent
current-branch commits need preserving in a merge commit.
4. Fast-forward versus true merge
flowchart LR A[commit A] --> B[commit B] B --> F[feature commit F] T1[trunk ref before] -. points to .-> B T2[trunk ref after fast-forward] -. moves to .-> F M[merge base M] --> L[trunk-only L] M --> R[feature-only R] L --> C[merge commit C] R --> C
In the upper graph, trunk can move directly from B to
F: the branch ref advances, but no extra merge commit is created. In
the lower graph, both sides contain unique commits. A true merge
records a new snapshot in C and gives C two parents, preserving both
lines of ancestry.
5. Three-way merge inputs
For the common two-branch case, Git conceptually compares three snapshots: the merge base, the current branch tip (“ours” in this merge context), and the incoming tip (“theirs”). If one side changed a region and the other did not, Git can usually combine the result automatically. If both sides made incompatible changes to the same logical region, Git cannot choose intent and records a conflict for human resolution.
| Input | Role |
|---|---|
| merge base | shared ancestor: what both sides started from |
current HEAD |
current branch's result since the base |
| incoming tip | branch/commit being merged |
6. A conflict is missing intent, not repository corruption
A merge conflict means Git found changes it cannot safely reconcile mechanically. During a conflicted merge, the index can hold multiple stages for an unmerged path and Git writes conflict markers into suitable working-tree files. The repository is in an in-progress merge state until you resolve and continue, or abort.
git status
git ls-files -u
git diff --name-only --diff-filter=U
Do not “fix” a conflict by deleting .git,
force-resetting indiscriminately, or accepting one side without
understanding the application behavior.
7. Deleting a branch deletes a name, not every commit it ever reached
git branch -d topic removes a branch ref when Git
considers it safely merged into the relevant upstream or
HEAD. If the commits are also reachable through
trunk, tags, other refs, or retention mechanisms, the
commit objects remain reachable. Even unreferenced objects are not
necessarily physically removed immediately.
git branch --merged
git merge-base --is-ancestor topic trunk
git branch -d topic
Force deletion with -D bypasses the merged-safety check
and can remove the easiest name for unmerged work. It is not needed
in the normal labs in this chapter.
8. Branching strategies are policies over the same graph mechanics
| Model | Useful when | Main cost |
|---|---|---|
| Trunk-based development | Frequent integration/deployment, strong automated tests, feature flags | Requires disciplined small changes and reliable CI |
| Short-lived feature branches | Review before integration without long isolation | Integration delay grows if branches live too long |
| Release branches | Multiple supported releases need selective stabilization/fixes | Backports and divergence management |
| Git Flow-style multi-branch model | Release trains/support constraints genuinely require multiple long-lived lanes | Higher merge/integration overhead and policy complexity |
None of these changes what a branch or merge is. They change how long refs live, who may update them, which histories are integrated, and how release support is organized.
9. DevOps connection — integration latency becomes delivery risk
A branch that diverges for hours usually produces fewer integration surprises than one that diverges for weeks. Branch policy therefore affects queue time, review size, conflict probability, release coordination, rollback reasoning, and how quickly CI feedback reaches the person who introduced a change. Choose the model from deployment frequency and support obligations, not popularity.
10. Knowledge check
Question 1. If trunk is an ancestor of
feature/api, what can a normal merge often
do?
trunk ref to the feature tip without
creating an extra merge commit.
Question 2. What three snapshots conceptually feed an ordinary two-head three-way merge?
Question 3. Does deleting a merged feature branch delete its
commits from trunk?
Question 4. Why is a long-lived branch usually more expensive to integrate?
11. Summary
Branches are movable refs over one commit graph. A fast-forward moves a ref when no divergent current history needs preservation. A true merge records a new multi-parent commit, using a merge base plus both tips to compute the result. Branching strategies are operational policies layered on these mechanics.
Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.