Rebase vs merge: shape and intent
Both rebase and merge integrate work from one branch into another. The difference is what they do to history. Merge creates a new commit with two parents that joins the histories at a Y-junction; both lines remain visible in the graph. Rebase replays the source branch's commits as new commits on top of the target's tip; the result is a single linear chain with no merge commit. Same content; very different shape.
The pragmatic decision tree. Use merge when the divergence is meaningful: integrating a long-running release branch, marking the completion of a feature as a unit of work, preserving the audit trail of "this came in from where" for compliance. Use rebase when the divergence is incidental — short-lived feature branches whose commits would clutter main with noise like "merge of main into feature/x." For most product teams shipping daily, rebase wins for feature branches because main reads as a single sequence of features rather than a tangle.
The team-policy form: pick one of three. (a) Linear history — every PR rebases onto main and merges fast-forward; main is one chain. (b) Always merge — every PR creates a merge commit even when fast-forward is possible (--no-ff); main has visible feature units. (c) Squash on merge — every PR's commits collapse into one commit on main; main is one commit per PR, regardless of internal structure. Each is internally consistent. Mixing them is the actual problem.
Two practical commands. git rebase main from a feature branch replays your local commits onto main's current tip. Conflicts pause the rebase like in interactive mode; resolve, git rebase --continue. git rebase --onto <new-base> <old-base> <branch> moves a branch from one base to another, useful when you branched off the wrong place originally.