---
name: git-branching-and-merging
description: Manage Git branches and integrate work — merge vs rebase decisions, tracking/remote branches, renaming branches (including master/main), and resolving merge conflicts including hard cases (whitespace conflicts, binary files, recurring conflicts with rerere, picking sides, subtree merges). Use when the user hits a merge conflict, asks "merge or rebase?", needs to rename or clean up branches, or must integrate diverged branches — even if they just paste conflict markers or a failed-merge error. Not for undoing already-made merges or recovering lost commits (use git-history-repair).
---

# Git branching and merging

Integrate diverged work and manage branches. Derived from Pro Git, 2nd ed.
(Chacon & Straub).

## Merge vs rebase

- Both produce the **same final snapshot**; only the history differs. Merge
  preserves what actually happened; rebase replays your commits for a linear
  story and lets the maintainer fast-forward.
- Book's recommended compromise: **rebase local changes to clean them up before
  pushing; never rebase anything you've pushed** that others may have based
  work on ("The Perils of Rebasing"). Rewritten public commits force everyone
  to re-merge and create duplicate commits in the log.
- If a partner *did* force-push rebased work: run `git rebase <their-branch>`
  or `git pull --rebase` instead of a plain pull — patch-id comparison lets Git
  drop your copies of the rewritten commits (works only when the rewritten
  patch is nearly identical).
- Useful forms: `git rebase <base> <topic>` (no checkout needed);
  `git rebase --onto master server client` (transplant only the commits unique
  to `client` since it diverged from `server`).

## Branch management

- `git branch --merged` / `--no-merged [ref]` — branches listed by `--merged`
  (other than the current one) are safe to delete with `-d`; `-d` refuses
  unmerged branches, `-D` forces and loses that work (recoverable via reflog).
- Tracking: `git checkout -b <br> origin/<br>`, `--track`, or bare
  `git checkout <br>` when the name matches exactly one remote. Change upstream
  anytime with `git branch -u origin/<br>`; `@{u}` refers to the upstream.
  `git branch -vv` shows ahead/behind — **cached since last fetch**; run
  `git fetch --all` first for real numbers.
- Push a branch under a different remote name: `git push origin local:remotename`.
  Delete remote branch: `git push origin --delete <br>`.
- Fetching never creates local editable branches — only remote-tracking refs
  you must branch from.
- Uncommitted changes that conflict with the target block `git checkout`;
  commit or stash first.

### Renaming a branch

`git branch --move old new`, then `git push --set-upstream origin new`, then
`git push origin --delete old`. **Do not rename branches others are actively
using.** Renaming `master`/`main` additionally breaks integrations, CI, scripts,
and host settings — before deleting the old name: update dependent projects,
test-runner/build/release configs, the host's default-branch and merge rules,
documentation references, and close or retarget pull requests against the old
branch.

## Resolving merge conflicts

1. Prefer a clean working directory before any risky merge (commit or stash) —
   it keeps `git merge --abort` reliable.
2. On conflict, `git status` lists unmerged paths. Edit files, remove all
   `<<<<<<<`/`=======`/`>>>>>>>` markers, then `git add <file>` to mark
   resolved, and `git commit` to conclude (`git mergetool` for a GUI walk-through).
3. Bail out anytime with `git merge --abort` (may be imperfect if you had
   unstashed changes when you started).
4. More context in the markers: `git checkout --conflict=diff3 <file>` re-writes
   markers with the common-ancestor version between `ours` and `theirs`
   (make it the default: `git config --global merge.conflictstyle diff3`).
   `--conflict=merge` restores plain markers to retry.
5. Understand *why* it conflicts:
   - `git log --oneline --left-right HEAD...MERGE_HEAD` — all commits involved
   - add `--merge` — only commits touching conflicted files; `-p` for their diffs
   - `git diff` mid-conflict shows combined-diff format (two ± columns: ours vs
     working copy, theirs vs working copy); after resolving, it previews the
     resolution; `git log --cc -p -1` shows how a past merge resolved conflicts.

### Hard cases

- **Whitespace-driven conflicts** (line-ending churn etc.): re-run the merge as
  `git merge -Xignore-space-change <branch>` (or `-Xignore-all-space`).
- **Pick a side per conflicted hunk without manual editing:**
  `git merge -Xours <branch>` / `-Xtheirs` — non-conflicting changes still
  merge; conflicts (including binary files) take the chosen side wholesale.
- **Fake merge:** `git merge -s ours <branch>` records a merge commit but keeps
  your content untouched — used to mark a branch as merged (e.g. a backport
  already applied) so a later real merge won't re-conflict. Do not confuse the
  `-s ours` *strategy* with the `-Xours` *option*.
- **Surgical re-merge of one file:** the index holds three stages —
  `:1:` common ancestor, `:2:` ours, `:3:` theirs:
  ```
  git show :1:file > file.base; git show :2:file > file.ours; git show :3:file > file.theirs
  # fix up any version (e.g. dos2unix), then:
  git merge-file -p file.ours file.base file.theirs > file
  ```
  Compare the candidate result with `git diff --ours` / `--theirs [-b]` / `--base`.
- **Recurring conflicts** (long-lived branch merged/rebased repeatedly): enable
  `git config --global rerere.enabled true`. Git records each conflict
  resolution ("Recorded preimage/resolution for FILE") and silently replays it
  next time ("Resolved FILE using previous resolution"). Inspect with
  `git rerere status` / `git rerere diff`; trial-merge a long-lived branch,
  resolve, back out with `git reset --hard HEAD^`, and the final merge or a
  later rebase reuses the stored resolutions.
- **Subtree merge** (one project mapped into a subdirectory of another): fetch
  the other project on its own branch, `git read-tree --prefix=sub/ -u
  <branch>` to plant it, and integrate updates with
  `git merge --squash -s recursive -Xsubtree=sub <branch>`. Diff against it
  with `git diff-tree -p <branch>` (normal diff is misleading here).

## Workflow patterns (choosing a branch structure)

- **Topic branches**: short-lived, one theme each; cheap to create/merge/discard
  several times a day; keep review units small.
- **Long-running tiers**: `master` only stable, `develop`/`next` integration,
  optionally `proposed` — merge upward as work stabilizes.

## Gotchas

- Deleted remote branches usually survive on the server until GC — recoverable.
- "origin" and "master" have no special meaning to Git — only defaults.
- A merge commit's **first parent** is the branch you were on; `-m 1` semantics
  and merge undo live in the git-history-repair skill.
- To see what a topic branch would introduce, `git diff master` is wrong after
  divergence (compares endpoints); use `git diff master...topic`.
