Merging Branches
Branches are only useful if their work can come back together. Merging takes the commits from one branch and combines them into another, usually bringing a finished feature into main. Most merges happen automatically in under a second; Git only asks for help when two branches changed the same lines, which is the topic of the next lesson.
In this lesson you will see the two kinds of merge Git performs, read the history graphs they create, and learn the options teams use to keep history tidy.
The basic command
You always merge into the branch you are on. To bring diwali-offer into main, first switch to main, then name the branch to merge:
git switch main
git merge diwali-offerWhat happens next depends on the shape of the history.
Fast-forward merge
While Priya worked on diwali-offer, nobody added commits to main. So main is simply behind: its latest commit is the parent of the branch's first commit. Git does not need to combine anything. It just moves the main label forward to the branch's last commit:
Updating 3aa8447..8b52b9c Fast-forward menu.html | 1 + offers.html | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 offers.html
git log --oneline -38b52b9c (HEAD -> main, diwali-offer) Link offers from the menu fcd55f3 Add Diwali offers page 3aa8447 Tweak styles
Both labels now point at the same commit and the history stays a straight line. No new commit was created. The branch's work is fully in main, so it can be deleted safely:
git branch -d diwali-offerDeleted branch diwali-offer (was 8b52b9c).
Three-way merge
Usually both branches move. Priya adds a footer on the footer-fix branch, and meanwhile commits a line about wedding orders directly on main. Now the history has split:
git log --oneline --graph --all -5* 0692245 (HEAD -> main) Mention bulk wedding orders * 8b52b9c Link offers from the menu * fcd55f3 Add Diwali offers page | * 4a599b4 (footer-fix) Add footer with shop address |/ * 3aa8447 Tweak styles
A fast-forward is impossible because neither branch contains the other. Git performs a three-way merge: it looks at three snapshots, the tip of each branch plus their merge base (the last commit they share, 3aa8447). For each file it works out what each side changed since the base and combines both sets of changes. The result is saved as a merge commit, a special commit with two parents.
git merge footer-fix -m "Merge branch 'footer-fix'"Merge made by the 'ort' strategy. index.html | 1 + 1 file changed, 1 insertion(+)
Without -m, Git opens your editor with this same message ready, and you just save and close. "ort" is the name of Git's default merge engine (it replaced the older "recursive" strategy in Git 2.34). The graph now shows the two lines joining:
git log --oneline --graph -6* 46c63d8 (HEAD -> main) Merge branch 'footer-fix' |\ | * 4a599b4 (footer-fix) Add footer with shop address * | 0692245 Mention bulk wedding orders * | 8b52b9c Link offers from the menu * | fcd55f3 Add Diwali offers page |/ * 3aa8447 Tweak styles
Each * is a commit and the lines show parents. The merge commit 46c63d8 has two parents: 0692245 (from main) and 4a599b4 (from footer-fix).
Which branches are merged?
git branch --merged lists branches whose commits are all included in the current branch, which makes them safe to delete. --no-merged shows the opposite: branches with work you have not merged yet.
git branch --merged
git branch -d footer-fixfooter-fix * main Deleted branch footer-fix (was 4a599b4).
Deleting a merged branch loses nothing: its commits are part of main's history now. Only the label goes away.
Always making a merge commit: --no-ff
Some teams want every feature to appear as a visible "bubble" in the history, even when a fast-forward was possible, so it is clear which commits belonged to which feature. --no-ff (no fast-forward) forces a merge commit:
git merge --no-ff about-page -m "Merge branch 'about-page'"
git log --oneline --graph -2Merge made by the 'ort' strategy. about.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 about.html * b57890b (HEAD -> main) Merge branch 'about-page' |\ | * 698154b (about-page) Add about page |/
The opposite option, --ff-only, refuses to merge unless a fast-forward is possible. It is handy when you only want to update a branch and never create a merge commit by accident.
Squash merging
A feature branch often has messy commits such as "Start gallery", "fix typo", "really fix it". --squash takes the combined result of all of them and stages it as a single change on the current branch, without creating a merge commit. You then commit it with one clean message:
git merge --squash gallery
git commit -m "Add gallery page"Updating b57890b..9851dee Fast-forward Squash commit -- not updating HEAD gallery.html | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 gallery.html [main d89c43a] Add gallery page 1 file changed, 2 insertions(+) create mode 100644 gallery.html
The history of main gets one tidy commit. The catch: Git does not record that gallery was merged, so git branch -d gallery will complain, and you must use -D once you are sure. GitHub offers the same choice ("Squash and merge") on pull requests, as you will see in Pull Requests & Code Review.
Common mistakes
- Merging in the wrong direction.
git merge featurebringsfeatureinto the current branch. Checkgit branch --show-currentfirst. - Merging with uncommitted changes. Commit or stash your work before merging so a problem does not mix with half-finished edits.
- Panicking at the editor. When Git opens an editor for the merge message, the default text is fine. Save and close it.
- Forgetting to delete merged branches. Old branches pile up and confuse everyone. Delete them with
git branch -dafter merging.
Key points
git merge <branch>brings that branch's commits into the branch you are on.- If the current branch has not moved, Git fast-forwards: it just moves the label, with no new commit.
- If both branches moved, Git does a three-way merge from the merge base and creates a merge commit with two parents.
--no-ffalways creates a merge commit;--ff-onlyrefuses anything but a fast-forward;--squashcombines a branch into one new commit.git branch --mergedshows branches that are safe to delete withgit branch -d.
Exercise
In a practice repository, create two branches from main: header and footer. Make one commit on each, changing different files. Merge header into main (watch for a fast-forward), then merge footer (watch for a merge commit). Show the graph and delete both branches.
Show solution
git switch -c header
git commit -am "Add site header"
git switch main
git switch -c footer
git commit -am "Add site footer"
git switch main
git merge header
git merge footer
git log --oneline --graph -4
git branch -d header footerThe first merge is a fast-forward because main had not moved. After that, main and footer have both moved, so the second merge is a three-way merge and Git creates a merge commit, visible as two lines joining in the graph.
Quick Quiz
1. When does Git perform a fast-forward merge?
If one branch simply contains the other, Git moves the label forward without a merge commit.
2. What is special about a merge commit?
A merge commit joins two lines of history, so it points to both branch tips.
3. What does git merge --squash feature do?
Squash produces one new ordinary commit; it does not record a merge.
4. Which command lists branches that are safe to delete because their work is already merged?
--merged shows branches fully contained in the current branch.
Learn Git with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
