How it works
The explanation, with examples you can run.
Fast-forward
Each commit below adds one file named after it. c is a small helper so the examples stay short.
git init -q -b main demo && cd democ() { echo "$1" > "$1.txt"; git add "$1.txt"; git commit -q -m "$1"; }c A; c Bgit switch -q -c feature; c Cgit switch -q maingit merge featureUpdating 6a57305..0605270Fast-forward C.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 C.txtmain was an ancestor of feature, so Git moved main to C. Many teams merge pull requests with --no-ff so each feature leaves a merge commit, or set git merge --ff-only to refuse anything but a fast-forward.
A true merge
Now let both branches move on:
git switch -q feature; c Dgit switch -q main; c Egit merge -q --no-edit featuregit log --oneline --graphgit cat-file -p HEAD | head -3* d01cd76 Merge branch 'feature'|\ | * ae76f44 D* | 142af93 E|/ * 0605270 C* 6a57305 B* 74bbda3 Atree 1bfd30eae5765ed9ea8d9cf2b38535fc1cdf1bb5parent 142af93c96d4bec1b8f29853a9b3dc30d99a1a67parent ae76f440ec825afc3e8b597e5ba34ea9a776ea9cThe merge commit has two parent lines. D and E keep their hashes; history shows exactly what happened, including that work went on in parallel.
Rebase instead
Undo that merge and rebase feature onto main instead:
git reset -q --hard HEAD~1git switch -q featuregit rev-parse --short HEADgit rebase maingit log --oneline --graphae76f44Successfully rebased and updated refs/heads/feature.* c1e1e0c D* 142af93 E* 0605270 C* 6a57305 B* 74bbda3 AD was ae76f44; now it is a different commit with E as its parent. The history is a straight line, and main can fast-forward to it. The original D still exists, reachable through the reflog, until garbage collection.
Choosing
| Merge | Rebase | |
|---|---|---|
| Existing commits | Untouched | Replaced by copies |
| History | Shows parallel work and when it was joined | Linear, as if done in sequence |
| Conflicts | Resolved once, in the merge commit | Possibly once per replayed commit |
| Safe on shared branches | Yes | No, unless everyone agrees |
A common rule: rebase your own unpublished work to keep it tidy (git pull --rebase, git rebase -i to squash fixups), and merge when combining branches other people use. git rebase --update-refs also moves any branches stacked on top of the one you rebase.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.