Introduction to Merging
Merging is how you bring two lines of work back together. You created a branch, did some work, and now you want those changes to flow back into main (or any other branch). Git has surprisingly clever algorithms for this — but the basic idea is simple: take the commits from one branch and apply them on top of another.
The big picture
Before the merge
A───B───C ◀── main
\
D───E ◀── featureAfter 'git merge feature' on main
A───B───C───────M ◀── main
\ /
D───E ◀── featureM is the merge commit — a special commit with two parents (C and E). It marks the point where the two branches reunited.
The basic workflow
Merge feature INTO main
# 1. Make sure your destination branch is up to date git switch main git pull # 2. Merge the feature branch in git merge feature # 3. Push git push
Notice the direction: you git switch to the destination branch (the one that should absorb the changes), then git merge the source branch you want to pull in.
Three kinds of merges
Fast-forward — the destination has not moved since the source branched off. Git can just slide the pointer forward. No merge commit is created.
Three-way merge (true merge) — both branches have new commits. Git creates a new merge commit with two parents.
Conflict — both branches changed the same lines. Git stops and asks you to resolve.
When Git can fast-forward
Fast-forward setup
A───B───C ◀── main (HEAD)
\
D───E ◀── feature
After 'git merge feature':
A───B───C───D───E ◀── main, featuremain simply moves forward to point at E. No merge commit is needed because feature already includes every commit on main. Fast-forward merges keep history linear.
When Git creates a merge commit
Three-way merge setup
A───B───C───F ◀── main (HEAD)
\
D───E ◀── feature
After 'git merge feature':
A───B───C───F───M ◀── main
\ /
D───E ◀── featureBecause main has commit F that feature does not, Git cannot fast-forward. It creates a merge commit M that combines the two timelines.
Why the “three-way” name?
To produce the merge, Git compares three snapshots: the common ancestor (here C), and each branch tip (F and E). It computes the changes each side made since C and combines them. The middle commit is what tells Git who changed what.
What about conflicts?
If both branches changed the same lines of the same file in different ways, Git cannot decide which version to keep. It stops the merge, marks the conflicting hunks in the files, and asks you to fix them. See the “Merge Conflicts” page for the recovery procedure.
The lifecycle of a feature
The full loop
# Start clean git switch main git pull # Branch off git switch -c feature-search # Work, commit, work, commit git add . && git commit -m "Add search component" git add . && git commit -m "Wire search to API" # Push for review git push -u origin feature-search # After PR is approved, merge (locally or on GitHub): git switch main git pull git merge feature-search # ← the moment git push # Clean up the merged branch git branch -d feature-search
Merging on GitHub vs locally
On GitHub you open a Pull Request and click “Merge”. Behind the scenes GitHub runs a merge on its servers.
GitHub offers three flavours: regular merge, squash merge, or rebase + fast-forward. Most teams pick one for the whole repo.
Locally,
git mergedoes the same thing but on your machine — useful for personal branches, hotfixes, or after a force-push when GitHub gets confused.
Merge vs rebase
Both bring work from one branch into another, but in different ways:
Merge keeps both timelines and adds a join commit. History becomes a graph.
Rebase replays your commits on top of the other branch. History becomes linear.
Some teams prefer merge for honesty (it reflects what happened); some prefer rebase for cleanliness. See the “Rebase vs Merge” page for the deep dive.
git status first. Merging on top of half-staged changes is the easiest way to confuse yourself.