# Merge vs rebase

> Keep the true shape of history, or replay your commits on top for a straight line — and never rebase shared commits.

Concept · Merging & history · https://levelcode.org/concepts/merge-vs-rebase

Your branch is behind `main`. There are two ways to bring in the new commits.

```text
A ─ B ─ E ─ F        ← main
    └ C ─ D          ← feature
```

**Merge** (`git merge main`, run on `feature`) adds a merge commit that joins the two histories:

```text
A ─ B ─ E ─ F
    └ C ─ D ─ M      ← feature
```

**Rebase** (`git rebase main`, run on `feature`) replays your commits **on top of** the new `main`, creating new commits `C'` and `D'`:

```text
A ─ B ─ E ─ F ─ C' ─ D'   ← feature
```

## Trade-offs

| | Merge | Rebase |
|---|---|---|
| History | True shape: what happened, when | Straight line, as if you'd started from the latest main |
| Commits | Unchanged; hashes stay the same | Rewritten: new hashes (see [the object model](https://levelcode.org/concepts/git-object-model)) |
| Conflicts | Resolved once, in the merge commit | Possibly once per replayed commit |
| Safe on shared branches | Yes | **No** |
| Reading `git log` and [bisecting](https://levelcode.org/concepts/git-bisect) | Noisier | Cleaner |

## The golden rule

> **Never rebase commits that other people have based work on.**

Rebasing replaces commits with new ones. If a teammate already has the old `C` and `D`, their history and yours have now diverged, and the next pull brings back duplicates or conflicts. Rebasing your **own unpushed** commits, or your own feature branch that nobody else uses, is fine.

When you do have to push a rebased branch, use

```bash
git push --force-with-lease
```

This refuses to overwrite the remote if someone pushed to it after your last fetch. Plain `--force` silently throws their work away.

## A common combination

- **Rebase locally** to keep your branch current and tidy. `git rebase -i main` lets you squash fixups, reword messages and reorder commits before review.
- **Merge into `main`,** via the platform's merge or squash button, so `main` itself is never rewritten.
- Set `git config --global pull.rebase true` so `git pull` on your feature branch rebases instead of adding "Merge branch 'main'" noise.

Nearly any rebase can be undone: the [reflog](https://levelcode.org/concepts/reflog) has the pre-rebase commits. How a team combines these is a [workflow](https://levelcode.org/concepts/trunk-based-development) decision, so agree on one.
