Pro Git Ch3 — Git Branching


  • Description: Notes on Pro Git (2nd ed.) Chapter 3 — branches as movable pointers, branch + merge basics, merge strategies + conflicts, branch management, branching workflows (long-running, topic, trunk-based), remote branches (tracking, upstream, pruning), and rebasing (incl. the golden rule).
  • My Notion Note ID: K2B-2-3
  • Created: 2020-06-02
  • Updated: 2026-05-19
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. Branches in a Nutshell

  • Branch = lightweight movable pointer to a commit.
  • Commit object = SHA-1 + author + message + tree pointer + parent pointer(s).
  • HEAD = pointer to the branch currently checked out.
  • Creating a branch = creating a new pointer (cheap — no copies).

1.1 Parent notation

  • <ref>^n — n-th parent (only meaningful for merge commits, which have >1 parent).
  • <ref>~n — first parent, n times (i.e., ~3 = ^^^).
  • ^ alone = ^1; ~ alone = ~1.
  • Combinable: HEAD~3^2 = grandparent's grandparent's second parent.
  • @{upstream} / @{u} — shorthand for the tracked upstream.
  • @{n} — n-th prior position of HEAD (reflog index).

2. Basic Branching and Merging

2.1 git branch

  • git branch — list branches; * marks current.
  • git branch <name> — create (doesn't switch).
  • git branch -v / -vv — last commit per branch (-vv shows tracking).
  • git branch --merged / --no-merged — relative to current HEAD.
  • git branch -d <name> — delete (refuses if unmerged).
  • git branch -D <name> — force delete.
  • git branch -m <old> <new> — rename.

2.2 git checkout / git switch

  • git checkout <branch> — switch.
  • git checkout -b <branch> — create + switch.
  • git checkout -b <branch> <start> — branch off specific commit/tag.
  • git checkout -b <local> <remote>/<branch> — create tracking branch.
  • git checkout -- <file> — discard worktree changes (legacy; see Ch 2 § 4).
  • Modern split (Git 2.23+):
    • git switch <branch> — switch only.
    • git switch -c <branch> — create + switch.
    • git restore <file> — discard changes (no overloading).

2.3 git merge

  • git merge <branch> — merge <branch> into current.
  • Strategies:
    • Fast-forward: target is an ancestor of source → just move pointer (no merge commit).
    • Three-way merge (recursive): find common ancestor + diverging tips → new merge commit with two parents.
  • git merge --no-ff <branch> — always create merge commit (preserves topic branch history).
  • git merge --ff-only — refuse non-fast-forward (CI safety).
  • git merge --squash <branch> — apply changes as a single staged set (no merge commit).

2.4 Conflicts

  • Markers in the working tree:
    • <<<<<<< ... ======= — current branch (HEAD).
    • ======= ... >>>>>>> — incoming branch.
  • Resolve manually → git add <file>git commit.
  • git merge --abort — cancel mid-merge.
  • git mergetool — open visual tool (configure: git config merge.tool <name>).
  • git checkout --ours <file> / --theirs <file> — pick a side wholesale.

3. Branch Management

  • git branch -u <remote>/<branch> — set upstream.
  • git branch -vv — show branch + upstream + ahead/behind.
  • Cleanup dead branches:
git fetch --prune
git branch --merged main | grep -v '^\* ' | xargs git branch -d

4. Branching Workflows

4.1 Long-running branches

  • Pattern: multiple permanent branches at different stability levels.
  • Classic GitFlow:
    • main (or master) — only stable release-ready code.
    • develop (or next) — integration; tested but not yet released.
    • proposed / pu (optional) — bleeding-edge experimental.
  • Commits "graduate" upward as they stabilize → merge develop → main on release.
  • Mental model: branches as work silos where commits flow from unstable to stable.

4.2 Topic branches

  • Short-lived branch per feature/bug.
  • Cheap to create + discard (Git's killer feature).
  • Enables:
    • Context-switch without losing work.
    • Per-topic code review (focused diff).
    • Multiple parallel approaches → keep best, discard rest.
  • Lifecycle:
git switch -c iss91
# work, commit
git switch main
git merge iss91          # or open a PR
git branch -d iss91

4.3 Integration vs trunk-based

  • Integration (GitFlow): topic → develop → main with release branches. Heavyweight; suits long release cycles.
  • Trunk-based: short topic branches merged frequently to main; release from main; favored for CI/CD shops. Uses feature flags for incomplete work.
  • All branching happens locally → no server roundtrip required.

5. Remote Branches

  • <remote>/<branch> = remote-tracking branch: local read-only pointer to remote state at last sync.
  • Updated by git fetch <remote> (or git pull).
  • Local and remote can diverge silently between fetches.
  • Example: origin/main may lag behind actual remote main if you haven't fetched.

5.1 Pushing

  • git push origin <branch> — push local <branch> to origin/<branch>.
  • Refspec long form: git push origin refs/heads/<branch>:refs/heads/<branch>.
  • Push to different name: git push origin <local>:<remote-name>.
  • First push with tracking: git push -u origin <branch> (sets upstream so future pull/push need no args).

5.2 Tracking branches (= upstream)

  • When set, git pull / git push operate against upstream without specifying remote/branch.
  • Auto-created by:
    • git clone (sets origin/mainmain).
    • git checkout -b <local> <remote>/<branch>.
    • git checkout --track <remote>/<branch>.
    • git checkout <branch> when name uniquely matches a remote.
  • Manual: git branch -u <remote>/<branch> on existing local branch.
  • Inspect: git branch -vv (shows tracking + ahead/behind).

5.3 Deleting remote branches

  • git push origin --delete <branch> (or legacy :refs/heads/<branch>).
  • Server keeps data until GC.

5.4 Pruning stale remote-tracking refs

  • After someone deletes a remote branch, your local origin/<branch> lingers.
  • git fetch --prune → cleans up.
  • git remote prune origin → same thing, explicit.

6. Rebasing

  • git rebase <base> — replay current branch's commits on top of <base>.
  • Produces linear history vs merge bubbles.
  • git rebase <base> <topic> — checkout topic then rebase.
  • git rebase --onto <newbase> <upstream> <branch> — surgical: take commits from <upstream>..<branch> and replay on <newbase> (extract a sub-branch from a chain).
  • Interactive: git rebase -i <base> — reorder/squash/edit/drop commits.
  • Conflicts during rebase: resolve → git addgit rebase --continue (or --skip / --abort).

6.1 Golden rule

  • Never rebase commits that exist outside your repository (i.e., that others may have based work on).
  • Rebasing rewrites SHAs → collaborators get duplicate history.

6.2 Rebase vs merge

  • Merge preserves topology (true history).
  • Rebase produces clean linear history.
  • Common team convention: rebase before pushing a topic branch, merge (or squash-merge) into main.

7. References