Chapter 06Lesson 05~125 minutes

Checkpoint Lab — Branches, Fast-Forward and Three-Way Merges, and Branching Strategies

Checkpoint the branch/merge model with mainline, feature and hotfix work, a fast-forward, a three-way merge, a conflict resolution, reachability checks, strategy comparison, and a small-team branching policy.

CheckpointFeature + hotfixGraph verificationBranch policy

Learning objectives

  • Build and verify a mainline/feature/hotfix graph with both fast-forward and true merge integration.
  • Predict merge eligibility and ref movement before running integration commands.
  • Engineer, inspect, and resolve a controlled merge conflict.
  • Prove integrated commits remain reachable after deleting merged topic refs.
  • Document a small-team branching policy and compare merge/squash/rebase alternatives without rewriting shared history.

1. Checkpoint scenario — a small team with feature and hotfix work

You will build one trunk mainline, integrate a short feature with a fast-forward, create a second feature that diverges while a hotfix advances trunk, integrate that feature with a true merge, then engineer and resolve one conflict. Everything stays in a disposable local repository.

2. Predictions before mutation

  1. If trunk does not move after creating feature/health, will git merge --ff-only feature/health create a new commit?
  2. If feature/alerts and trunk each gain a different commit after their common base, can --ff-only integrate the feature?
  3. After deleting a merged feature branch, should its integrated commits still be reachable from trunk?
  4. If both sides edit the same policy line differently, what state should Git enter?

3. Setup and preflight

Git Bash / Bash / zsh

mkdir git-branch-checkpoint
cd git-branch-checkpoint
git init -b trunk
git config user.name "Branch Checkpoint"
git config user.email "branch-checkpoint@example.invalid"
printf "service=api
health=none
" > service.conf
printf "policy=balanced
" > deploy.conf
git add service.conf deploy.conf
git commit -m "Create deployment baseline"
git status --short --branch
git log --graph --decorate --oneline --all

4. Feature 1 — observe a fast-forward integration

git switch -c feature/health
printf "service=api
health=/healthz
" > service.conf
# PowerShell: @('service=api','health=/healthz') | Set-Content service.conf

git add service.conf
git commit -m "Expose API health endpoint"
HEALTH=$(git rev-parse HEAD)

git switch trunk
git merge-base --is-ancestor trunk feature/health
git merge --ff-only feature/health
git rev-parse trunk
git log --graph --decorate --oneline --all

Expected: trunk moves to the existing health commit. No two-parent merge commit is created.

5. Feature 2 and hotfix — create divergence

git switch -c feature/alerts
printf "alerts=latency
" > alerts.conf
# PowerShell: Set-Content alerts.conf 'alerts=latency'
git add alerts.conf
git commit -m "Add latency alert configuration"
ALERTS=$(git rev-parse HEAD)

git switch trunk
git switch -c hotfix/timeout
printf "request_timeout=30s
" > timeout.conf
# PowerShell: Set-Content timeout.conf 'request_timeout=30s'
git add timeout.conf
git commit -m "Set production request timeout"
HOTFIX=$(git rev-parse HEAD)

git switch trunk
git merge --ff-only hotfix/timeout
git branch -d hotfix/timeout

git log --graph --decorate --oneline --all
git merge-base trunk feature/alerts

The feature and trunk now diverge from the health commit: trunk contains the hotfix; the feature contains the alert commit.

6. Predict failure of --ff-only, then create the true merge

git merge --ff-only feature/alerts
# Expected: refusal because histories have diverged.

git merge feature/alerts -m "Merge latency alerts"
MERGE_ALERTS=$(git rev-parse HEAD)
git cat-file -p HEAD
git rev-parse HEAD^1
git rev-parse HEAD^2
git log --graph --decorate --oneline --all

The first command is a safe precondition check: it refuses without integrating. The second creates a two-parent merge commit because a fast-forward cannot preserve both lines.

7. Engineer and inspect a small conflict

git switch -c feature/strict-deploy
printf "policy=strict
" > deploy.conf
# PowerShell: Set-Content deploy.conf 'policy=strict'
git add deploy.conf
git commit -m "Require strict deployment policy"

git switch trunk
printf "policy=conservative
" > deploy.conf
# PowerShell: Set-Content deploy.conf 'policy=conservative'
git add deploy.conf
git commit -m "Prefer conservative deployment policy"

git merge feature/strict-deploy
git status
git diff --name-only --diff-filter=U
git ls-files -u

Both sides edited the same logical line differently, so Git cannot infer the desired policy.

8. Resolve the conflict intentionally

Choose the team-approved result explicitly. For this disposable lab, write policy=strict-with-fallback:

printf "policy=strict-with-fallback
" > deploy.conf
# PowerShell: Set-Content deploy.conf 'policy=strict-with-fallback'

git add deploy.conf
git status
git diff --staged
git merge --continue
git log --graph --decorate --oneline --all

If the editor opens, keep or improve the merge message so it explains the integration decision. The staged file is the actual proposed merge snapshot.

9. Delete merged topic refs and verify reachability

git merge-base --is-ancestor "$HEALTH" trunk
git merge-base --is-ancestor "$ALERTS" trunk
git branch --merged trunk
git branch -d feature/health
git branch -d feature/alerts
git branch -d feature/strict-deploy
git branch --list
git log --graph --decorate --oneline --all

The graph remains; only the short-lived names disappear.

10. Compare graph shapes without rewriting this shared-style checkpoint

Alternative Conceptual result What you would lose/change
True merge (used here) Two-parent integration commit Keeps feature ancestry and graph divergence visible
Squash integration One new single-parent commit with combined feature content Feature commits are not ancestors of trunk
Rebase then fast-forward Linear sequence replayed on updated trunk Feature commit IDs change during rebase

Do not rewrite the checkpoint history just to produce all three shapes in one repository. Chapter 09 provides controlled rebase labs. The learning goal here is to choose a shape deliberately before integration.

11. Write a small-team branching policy

Create BRANCHING_POLICY.md with rules like these, adjusted to your own delivery constraints:

# Branching policy
- trunk is the only long-lived development branch and should remain releasable.
- feature/* and fix/* branches should normally live less than 2 working days.
- integrate only after tests and review pass.
- prefer fast-forward when no explicit integration record is required.
- use a merge commit when preserving branch ancestry/integration context is valuable.
- create release branches only when multiple supported release lines require them.
- never force-rewrite shared trunk as routine cleanup.
git add BRANCHING_POLICY.md
git diff --staged
git commit -m "Document short-lived branch policy"

12. Produce a compact verification report

git status --short --branch
git branch --list
git log --graph --decorate --oneline --all
git rev-list --merges --oneline trunk
git show --no-patch --format=raw "$MERGE_ALERTS"
git merge-base --is-ancestor "$HOTFIX" trunk

This answers: Is the repository clean? Which refs remain? Where are merge commits? Did the alerts merge have two parents? Is the hotfix still in trunk ancestry?

13. Verification checklist

  • One feature integration was a fast-forward with no extra merge commit.
  • The hotfix advanced trunk before the second feature integrated.
  • feature/alerts could not be fast-forwarded into trunk after divergence.
  • The alerts integration produced a two-parent merge commit.
  • The deployment-policy conflict was inspected before resolution.
  • The resolved file was staged and reviewed before completing the merge.
  • Merged short-lived branch refs were deleted with -d, not forced.
  • Integrated feature/hotfix commit OIDs remain ancestors of trunk.
  • A branching policy was documented from actual team constraints.
  • No shared-history rewrite or destructive cleanup command was used.

14. Cleanup

Git Bash / Bash / zsh

git status --short --branch
cd ..
pwd
rm -rf git-branch-checkpoint

PowerShell

git status --short --branch
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-branch-checkpoint

15. Knowledge check

Question 1. Why did the health integration not create a merge commit?

Question 2. Why did --ff-only refuse the alerts integration?

Question 3. After deleting feature/alerts, how can its work still be present?

Question 4. A merge reports no textual conflict but tests fail. Is that contradictory?

Question 5. Why not rebase this already integrated checkpoint merely to make the graph linear?

16. What Chapter 06 adds to a production Git operating model

You can now reason about branch refs, ancestry, merge bases, fast-forward eligibility, true merge commits, conflict state, safe branch deletion, and the operational consequences of branch lifetime. That lets you design integration policy around release/support constraints rather than command folklore.

17. Chapter checkpoint summary

A branch is a movable ref, a merge is a graph integration, and a strategy is a governance choice. Short feedback loops and explicit integration rules generally reduce delivery risk; long-lived branches are justified only when the product's release/support model requires them.

Next chapter

Remotes, Refspecs, Fetch, Pull, Push, SSH, HTTPS, and Protocol Behavior

Chapter 07 takes the branch/ref model across repository boundaries. You will distinguish local branches from remote-tracking refs and prove exactly what fetch, pull, and push move.

Authoritative references

 git-branch
 git-switch
 git-merge
 git-merge-base

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.