Git Handbook

#Branching and merging

A branch is a pointer. Everything difficult here is about what happens when two pointers have diverged and you want one history again.


#1 · Three ways to combine

gitGraph
    commit id: "A"
    commit id: "B"
    branch feature
    commit id: "C"
    commit id: "D"
    checkout main
    commit id: "E"
    merge feature id: "M"
Fast-forwardMerge commitRebase
When possibleTarget hasn't movedAlwaysAlways
New commit?NoneOne, with two parentsNew copies of each commit
History shapeStraight lineBranching, then joiningStraight line
Preserves original hashesYesYesNo
Shows that a branch existedNoYesNo
Safe on shared branchesYesYesNo

#Fast-forward

If main has not moved since you branched, git can just slide the pointer forward. No merge commit, no possible conflict.

git merge feature            # fast-forwards when it can
git merge --no-ff feature    # force a merge commit anyway

--no-ff is worth knowing: it keeps a visible record that a feature branch existed, which some teams want for auditability.

#Merge

Creates a commit with two parents. Nothing is rewritten; both histories survive exactly as they were.

#Rebase

Replays your commits on top of the target, creating new commits with new hashes.

git rebase main              # move my branch onto the current main

Rebase does not move commits — it copies them. The originals stay in the object store until gc, which is why the reflog can undo a bad rebase. It is also why the hashes change, and therefore why rebasing shared history is destructive: your teammate's copy and yours are now different objects.


Interactive simulation — needs JavaScript.


#2 · The rule

Rebase your own unpushed work. Merge everything else.

SituationDo
My local branch is behind mainRebase — keeps history linear
Tidying my commits before a PRRebase -i — squash the "wip" noise
Bringing a reviewed feature into mainMerge (often squash-merge)
Someone else has the branch tooMerge. Never rebase
main into my long-lived branchMerge, or rebase if you are the only one on it

The Golden Rule of Rebasing: never rebase commits that exist outside your repository. If it has been pushed and anyone may have pulled it, merge.


#3 · Conflicts

A conflict is not an error. It means two changes touched the same region and git will not guess.

<<<<<<< HEAD
the version on the branch you are ON
=======
the version being merged IN
>>>>>>> feature
# 1. see what conflicts
git status

# 2. edit the files -- remove ALL the markers
# 3. mark resolved
git add <file>

# 4. finish
git commit          # for merge
git rebase --continue   # for rebase
Escape hatchCommand
Abandon a mergegit merge --abort
Abandon a rebasegit rebase --abort
Take their whole filegit checkout --theirs <file>
Take my whole filegit checkout --ours <file>

During a rebase, "ours" and "theirs" are reversed from what you expect. Rebase replays your commits onto the target, so the target is "ours" and your commit is "theirs". This catches everyone once — check with git status rather than guessing.

rerere (reuse recorded resolution) is worth enabling if you rebase long branches repeatedly — git remembers how you resolved a conflict and reapplies it:

git config --global rerere.enabled true

#4 · Branching strategies

StrategyBranchesGood forCost
Trunk-basedmain + very short-livedCI/CD, frequent deploysNeeds feature flags and good tests
GitHub flowmain + feature branches, PR to mergeMost teams, web servicesLittle; the sensible default
Git flowmain, develop, feature/*, release/*, hotfix/*Versioned software with support branchesHeavy — often more ceremony than value
Release branchesmain + release/x.ySoftware you must patch old versions ofCherry-picking between branches

Most teams should use GitHub flow and most who use git flow do not need it. Git flow was designed for shipping versioned desktop software with parallel supported releases. If you deploy from main several times a week, develop is an extra merge that buys nothing.

Long-lived branches are the real problem, whatever the strategy. A branch open for three weeks accumulates conflicts superlinearly. Merge main in often, or keep branches under a few days.


#5 · Squash, merge, or rebase on the PR button

GitHub and GitLab offer three merge buttons. They are not interchangeable.

ButtonResult on mainUse when
Create a merge commitEvery branch commit, plus a merge commitYou want the full development record
Squash and mergeOne commit containing everythingThe usual default — the branch's "wip" commits are noise
Rebase and mergeEach commit replayed, no merge commitYou curated the commits and each is meaningful

Squash-merge is the right default for most teams. It makes main a clean list of features, each revertible in one command, and it stops "fix typo" appearing in the permanent history. The trade-off: you lose the fine-grained history of how the feature was built — which is usually not worth preserving.


#6 · Interview questions

QuestionAnswer
⭐ "Merge or rebase?"Rebase my own unpushed work to keep history linear; merge anything shared. Rebase copies commits with new hashes, so rebasing pushed history makes everyone else's copies diverge.
⭐ "What is a fast-forward?"When the target hasn't moved since you branched, git slides the pointer forward — no merge commit and no possible conflict. --no-ff forces one anyway if you want the branch recorded.
"What actually causes a conflict?"Two changes to the same region of the same file with no common resolution. Git marks the region and refuses to guess. It is not an error state — abort or resolve.
⭐ "Why are ours/theirs swapped in a rebase?"Because rebase replays your commits onto the target, so the target is the current checkout — "ours" — and your commit is being applied — "theirs".
"Which merge button?"Squash by default: main becomes one clean commit per feature, each revertible. Merge commit if the branch history matters. Rebase-and-merge only if every commit was curated.
"Git flow or trunk-based?"Trunk-based or GitHub flow for anything deployed continuously. Git flow suits versioned software with supported old releases; using it for a web service is ceremony without benefit.

#Stop condition

You have this when you can:

  1. explain what fast-forward, merge and rebase each do to the graph,
  2. state the golden rule of rebasing and why hashes change,
  3. resolve a conflict and abort one,
  4. explain the ours/theirs inversion during rebase, and
  5. choose a merge button with a reason.