Skip to content
Laviyx Learn

Try

Git and GitHub course menu

git merge Explained: Fast-Forward and Three-Way Merges

Combine branches with git merge. Understand fast-forward and three-way merges, merge commits, --no-ff, squash merges, how to abort a merge, and cleaning up afterwards.

Beginner8 min readUpdated
On this page

git merge brings the work from another branch into the current branch. So first switch to the branch that should receive the work, usually main. There are two kinds of merge.

Fast-forward merge#

Here add-cart has a new commit, and main has not moved since the branch started:

Terminal
git switch -q -c add-cart
echo "Cart page" > cart.html
git add .
git commit -q -m "Add cart page"
git switch -q main
git log --oneline --graph --all
Output
* 0f0e747 Add cart page
* 4e04c7b Add home page

Nothing has happened on main, so Git only needs to move the main label forward:

Terminal
git merge add-cart
Output
Updating 4e04c7b..0f0e747
Fast-forward
 cart.html | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 cart.html
Text
Before:  A   main
          \
           B   add-cart

After:   A <--- B   main, add-cart

No new commit is created. The branch is finished, so delete it:

Terminal
git branch -d add-cart
Output
Deleted branch add-cart (was 0f0e747).

Three-way merge#

Now both branches have new commits. Git cannot just move a label, so it makes a merge commit with two parents.

Terminal
git switch -q -c add-search
echo "Search page" > search.html
git add .
git commit -q -m "Add search page"
git switch -q main
echo "Contact page" > contact.html
git add .
git commit -q -m "Add contact page"
git log --oneline --graph --all
Output
* a294284 Add search page
| * 28d92ec Add contact page
|/  
* 0f0e747 Add cart page
* 4e04c7b Add home page
Terminal
git merge --no-edit add-search
git log --oneline --graph
Output
Merge made by the 'ort' strategy.
 search.html | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 search.html
*   56bc1a5 Merge branch 'add-search'
|\  
| * a294284 Add search page
* | 28d92ec Add contact page
|/  
* 0f0e747 Add cart page
* 4e04c7b Add home page

It is called three-way because Git compares three commits: the tip of each branch and their common ancestor. Without --no-edit, Git opens your editor so you can confirm the message Merge branch 'add-search'.

Forcing a merge commit#

Some teams like every feature to show up as a merge commit, even when a fast-forward was possible:

Terminal
git merge --no-ff add-cart

This keeps a visible record of where a feature began and ended.

Squash merging#

--squash takes all the changes on a branch and stages them as one change, for you to commit as a single commit:

Terminal
git branch -d add-search
git switch -q -c fix-title
echo "Title 1" >> index.html
git commit -q -am "Try title"
echo "Title 2" >> index.html
git commit -q -am "Fix title again"
git switch -q main
git merge --squash fix-title
git commit -q -m "Improve page title"
git log --oneline -n 2
Output
Deleted branch add-search (was a294284).
Updating 56bc1a5..9354a08
Fast-forward
Squash commit -- not updating HEAD
 index.html | 2 ++
 1 file changed, 2 insertions(+)
1011687 Improve page title
56bc1a5 Merge branch 'add-search'

The two messy commits became one clean commit on main. GitHub offers this as a button on pull requests. Because the branch's commits were not merged directly, git branch -d will refuse to delete it, so use -D.

Which branch receives the changes?#

Always the one you are on. To check before merging:

Terminal
git branch --show-current
Output
main

Stopping a merge#

If a merge goes wrong or you change your mind while it is in progress, this returns everything to how it was before the merge began:

Terminal
git merge --abort

Common mistakes#

Practice#

Practice exercise

Create a branch docs, add and commit README.md, return to main, add and commit LICENSE, and then merge docs into main. Print the graph.

Show solution
Terminal
git switch -q -c docs
echo "# App" > README.md
git add . && git commit -q -m "Add README"
git switch -q main
echo "MIT" > LICENSE
git add . && git commit -q -m "Add license"
git merge -q --no-edit docs
git log --oneline --graph
Output
*   5a289e4 Merge branch 'docs'
|\  
| * 217e5f2 Add README
* | 5ad2cc7 Add license
|/  
* c17a865 Start

Key takeaway#

Next: Resolving Merge Conflicts.

Finished this lesson?