Chapter 06Lesson 02~115 minutes

Branches, Fast-Forward and Three-Way Merges, and Branching Strategies: Guided Hands-On Workflow and Core Operations

Create and inspect branches with modern switch syntax; perform fast-forward and true merges; engineer, abort, resolve, and continue a conflict; and verify reachability before deleting merged branches.

git switchgit mergeConflict resolutionReachability

Learning objectives

  • Create and switch branches with git branch and git switch without relying on legacy checkout syntax.
  • Perform and verify both fast-forward and non-fast-forward merges.
  • Inspect merge bases, parent OIDs, refs, and graph shape before and after integration.
  • Create a conflict, inspect unmerged index state, abort safely, then resolve and continue.
  • Delete merged short-lived branch refs only after proving their commits remain reachable.

1. Create a disposable branch-and-merge lab

Git Bash / Bash / zsh

mkdir git-branch-lab
cd git-branch-lab
git init -b trunk
git config user.name "Branch Lab"
git config user.email "branch-lab@example.invalid"
printf "service=baseline
" > service.conf
git add service.conf
git commit -m "Create service baseline"
git status --short --branch
git log --graph --decorate --oneline --all

PowerShell file-creation alternative

New-Item -ItemType Directory git-branch-lab | Out-Null
Set-Location git-branch-lab
git init -b trunk
git config user.name "Branch Lab"
git config user.email "branch-lab@example.invalid"
Set-Content service.conf 'service=baseline'
git add service.conf
git commit -m "Create service baseline"
git status --short --branch
git log --graph --decorate --oneline --all

2. Create and switch branches with modern commands

git branch
git switch -c feature/healthcheck
git branch --show-current
git show-ref --heads

git switch -c creates a branch at the chosen start point and attaches HEAD to it transactionally. Older material often uses git checkout -b feature/healthcheck; it remains common compatibility syntax, but switch separates branch switching from path restoration more clearly.

3. Make feature work while trunk stays still

Git Bash / Bash / zsh

printf "service=baseline
healthcheck=/healthz
" > service.conf
git status --short
git diff -- service.conf
git add service.conf
git commit -m "Add service health check"
FEATURE_FF=$(git rev-parse HEAD)
git log --graph --decorate --oneline --all

Because trunk has not moved, its tip is an ancestor of the feature tip.

4. Predict and verify a fast-forward

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

Prediction: trunk moves to the existing feature commit. No new merge commit is created. --ff-only turns that graph assumption into an enforced precondition.

5. Delete the merged branch, then prove the commit remains

git merge-base --is-ancestor feature/healthcheck trunk
git branch -d feature/healthcheck
git show-ref --heads
git cat-file -e "$FEATURE_FF^{commit}"

The feature ref is gone, but the feature commit is now the trunk tip and remains reachable. On PowerShell capture the OID as $FEATURE_FF = git rev-parse HEAD and use git cat-file -e "$FEATURE_FF^{{commit}}".

6. Create deliberate divergence for a true merge

Git Bash / Bash / zsh

git switch -c feature/metrics
printf "metrics=true
" > metrics.conf
git add metrics.conf
git commit -m "Add metrics configuration"
FEATURE_METRICS=$(git rev-parse HEAD)

git switch trunk
printf "runbook: health check is /healthz
" > runbook.md
git add runbook.md
git commit -m "Document service health check"
TRUNK_DOC=$(git rev-parse HEAD)

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

Both tips now have unique commits. The merge base should be the earlier health-check commit.

7. Create a non-fast-forward merge commit

git status --short --branch
git merge feature/metrics -m "Merge metrics feature"
MERGE_METRICS=$(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 new commit should contain two parent lines. Parent 1 is the previous trunk tip; parent 2 is the merged feature tip in this simple local case.

8. Engineer a small conflict

Git Bash / Bash / zsh

printf "mode=balanced
" > policy.conf
git add policy.conf
git commit -m "Add policy baseline"

git switch -c feature/strict-policy
printf "mode=strict
" > policy.conf
git add policy.conf
git commit -m "Use strict policy mode"

git switch trunk
printf "mode=conservative
" > policy.conf
git add policy.conf
git commit -m "Use conservative policy mode"

git merge-base trunk feature/strict-policy
git merge feature/strict-policy

The final merge should stop with a conflict because both sides changed the same line differently from the merge base.

9. Inspect the conflict, then abort once

git status
git diff --name-only --diff-filter=U
git ls-files -u
git diff -- policy.conf

git merge --abort
git status --short --branch
git log --graph --decorate --oneline --all

git merge --abort attempts to reconstruct the pre-merge state. This is why starting from a clean working tree is a strong safety habit: unrelated uncommitted changes can make recovery from an integration attempt harder.

10. Re-run, resolve, and continue

git merge feature/strict-policy
printf "mode=strict-with-fallback
" > policy.conf
# PowerShell: Set-Content policy.conf 'mode=strict-with-fallback'

git add policy.conf
git status
git diff --staged

git merge --continue

git merge --continue completes the merge after conflicts are resolved and staged; Git may open the configured editor for the merge message. In automation or a teaching validator, the equivalent final commit can be made non-interactively after resolution, but humans should review the message and staged result.

11. Delete merged topic refs only after reachability checks

git merge-base --is-ancestor feature/metrics trunk
git merge-base --is-ancestor feature/strict-policy trunk
git branch --merged trunk
git branch -d feature/metrics
git branch -d feature/strict-policy
git log --graph --decorate --oneline --all

The commits remain in the trunk ancestry even though the short-lived branch names disappear.

12. Challenge — choose the graph operation from the requirement

  1. Verify whether a branch can be integrated without creating a merge commit.
  2. Find the shared base of two divergent tips.
  3. Stop an in-progress conflicted merge and return to the pre-merge state.
  4. List branches whose tips are already reachable from trunk.
  5. Prove an OID is still reachable after deleting its feature branch.

Select among merge-base --is-ancestor, merge-base, merge --abort, branch --merged, and reachability/object inspection rather than memorizing one sequence.

13. Cleanup

Git Bash / Bash / zsh

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

PowerShell

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

14. Knowledge check

Question 1. What changed during the fast-forward merge?

Question 2. Why did the metrics merge need a new commit?

Question 3. What does git merge --abort do?

Question 4. Why is git branch -d safer than -D?

15. Summary

You created refs without copying history, fast-forwarded one branch, created a true merge for divergent history, inspected merge-base/parents, aborted and then resolved a conflict, and removed merged branch names only after proving reachability.

Next

Decide which graph shape and branch lifetime your team should prefer

Lesson 3 examines configuration and policy: fast-forward defaults, pull integration, branch-specific merge options, naming/lifecycle rules, squash/merge/rebase tradeoffs, and the operational cost of long-lived branches.

Authoritative references

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

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.