Lesson 03 of 8 · Level 1 — Foundations
Branching and merging
Create branches for every change, bring them back with a fast-forward, a merge commit or a rebase, understand which history each produces, squash work-in-progress commits, and resolve conflicts calmly.
Branches are cheap, so use them
Every change gets its own short-lived branch:
$ git switch -c fix/order-404
# edit, commit, commit
$ git push -u origin fix/order-404 # share it and open a pull request (lesson 04)
Short-lived branches (hours to a couple of days) merge cleanly; long-lived branches drift and collect conflicts.
Branching is taking a photocopy of a shared document to try out your edits in peace. Merging is carefully writing your edits back into the original, and a conflict is when someone else changed the same sentence while you were busy.
Three ways to bring a branch back
Before: main: A---B---C
\
feature: D---E
Fast-forward (only possible if main hadn't moved): A---B---D---E
Merge commit: A---B---C-------M (M has two parents: C and E)
\ /
D---E
Rebase + fast-forward: A---B---C---D'---E' (D and E replayed as new commits)
| Method | History looks like | Good for |
|---|---|---|
| Fast-forward | A straight line | Tiny changes, up-to-date branches |
| Merge commit | Branches visible, with merge points | Keeping the context of a feature branch |
| Rebase then fast-forward | A straight line, feature commits on top | Clean linear history on the main branch |
| Squash merge (on the Git server) | One commit per pull request | Simple history; WIP commits vanish |
Pick one convention per repository and set it on the Git server (allowed merge methods on pull requests), so history stays consistent.
Rebase: replay your work on the latest main
$ git switch fix/order-404
$ git fetch origin
$ git rebase origin/main # your commits are replayed on top of the latest main
$ git push --force-with-lease # your branch was rewritten; only you use it
--force-with-lease refuses to overwrite the remote branch if someone else pushed to it meanwhile, unlike a plain --force.
Interactive rebase tidies your commits before review:
$ git rebase -i HEAD~4
pick 1a2b3c4 add order lookup
squash 5d6e7f8 wip
squash 9a8b7c6 fix typo
reword 0f1e2d3 handle missing orders
Resolving conflicts
$ git merge main
CONFLICT (content): Merge conflict in values-prod.yaml
$ git diff --name-only --diff-filter=U
values-prod.yaml
resources:
<<<<<<< HEAD
requests: { cpu: 500m, memory: 512Mi }
=======
requests: { cpu: 250m, memory: 768Mi }
>>>>>>> main
- Decide the correct result, which is often a combination (here maybe
cpu: 500m, memory: 768Mi), not just "mine" or "theirs". - Remove the markers, save, run the tests or a quick check.
git add values-prod.yamlandgit merge --continue(orgit rebase --continue).- Unsure?
git merge --abortreturns you to where you started.
Set git config --global merge.conflictStyle zdiff3 to also see the common ancestor's version inside the markers, which makes the intent of each side much clearer.
Try it: all three merges
- On a test repository, create
featurewith two commits whilemainstays still; merge with--ff-onlyand look atgit log --graph. - Repeat with a new commit on
mainfirst; merge with--no-ffand look again. - Repeat once more but
git rebase mainon the feature branch, then fast-forward. - Create a conflict on purpose (same line changed on both branches), resolve it with
zdiff3markers on, and finish the merge. - Squash three WIP commits into one with
git rebase -i.
Recap
- One short-lived branch per change.
- Fast-forward, merge commit, rebase or squash: know the history each produces and pick one convention.
- Rebase only what hasn't been shared; push rewrites with
--force-with-lease. - Conflicts: decide the correct result, remove markers, test,
add,continue;--abortis always available.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.