How to Merge Two Branches in Git: A Complete Guide
Merging branches is one of the most common — and most misunderstood — operations in Git. Whether you're combining a finished feature into your main codebase or pulling in updates from a teammate's work, understanding how Git handles merges puts you in control of your project history.
What Does "Merging" Actually Mean in Git?
When you merge two branches, Git combines the changes from both into a single branch. Think of branches as parallel timelines of your project. Merging brings those timelines back together.
Git doesn't just paste one branch's changes on top of another. It looks at three points: the common ancestor (where the two branches originally diverged), the tip of the first branch, and the tip of the second. This is called a three-way merge, and it's how Git figures out what changed, what's new, and what might conflict.
The Basic Merge Command
To merge one branch into another, the core process is:
- Switch to the branch you want to merge into — typically
mainordevelop - Run the merge command pointing at the branch you want to bring in
git checkout main git merge feature-branch That's it for the basic operation. Git will attempt to combine the histories automatically.
Two Types of Merges Git Can Perform
Not every merge works the same way under the hood.
Fast-Forward Merge
If the branch you're merging into hasn't changed since the feature branch was created, Git can simply move the pointer forward. No new commit is created — Git just "fast-forwards" the timeline.
This produces a clean, linear history, which looks tidy but hides the fact that a separate branch ever existed.
Three-Way Merge (Merge Commit)
If both branches have new commits since they diverged, Git creates a merge commit — a special commit with two parent commits. This preserves the full branching history.
You can force a merge commit even when fast-forward is possible using:
git merge --no-ff feature-branch Many teams prefer this because it makes the branching structure explicit in the log.
| Merge Type | When It Happens | Creates New Commit | Preserves Branch History |
|---|---|---|---|
| Fast-Forward | No new commits on target branch | No | No |
| Three-Way | Both branches have diverged | Yes | Yes |
--no-ff | Forced, any situation | Yes | Yes |
What Happens When There's a Conflict 🔀
A merge conflict occurs when both branches changed the same part of the same file in different ways. Git can't decide which version to keep, so it pauses and marks the problem in the file itself.
You'll see something like:
<<<<<<< HEAD This is the version from main ======= This is the version from feature-branch >>>>>>> feature-branch To resolve it:
- Open the conflicted file(s)
- Decide which version to keep — or write a combined version
- Remove the conflict markers (
<<<<<<<,=======,>>>>>>>) - Stage the resolved files:
git add filename - Complete the merge:
git commit
Git will pre-populate a merge commit message, or you can write your own.
Alternative: Rebasing Instead of Merging
Rebasing is an alternative to merging that rewrites history rather than creating a merge commit. Instead of combining histories, it replays the commits from one branch on top of another.
git checkout feature-branch git rebase main This gives you a linear history without merge commits — but it changes commit hashes, which can cause problems if others are working on the same branch. The general rule: rebase local, unpublished work; merge shared branches.
Before You Merge: A Few Things Worth Checking
Merging isn't inherently risky, but going in without context can create unnecessary headaches.
- Pull the latest changes on both branches before merging, so you're not working with stale code
- Review the diff between branches using
git diff main..feature-branchto understand what's about to come in - Check for open work on the feature branch — unfinished changes or debug code left behind
- Know your team's branching strategy — whether your project uses Git Flow, trunk-based development, or something else shapes when and how merges should happen
How Workflow Affects the Right Merge Approach
The "correct" way to merge isn't universal — it depends heavily on how your project is organized and who's working on it.
A solo developer on a small project might prefer fast-forward merges for a clean, minimal history. A team using pull requests will usually generate merge commits automatically through their platform (GitHub, GitLab, Bitbucket), sometimes with squash options to compress multiple commits into one. An open source project might have strict conventions about rebasing before merging to maintain a readable log.
Squash merging is a third path some teams use — it takes all the commits from a branch and combines them into a single commit before merging. This produces clean history but loses the granular commit-by-commit record of how a feature was built.
git merge --squash feature-branch git commit Each approach makes a different trade-off between history clarity, granularity, and simplicity.
Your History Tells a Story 📖
Every merge decision shapes what your project's Git log looks like months or years from now. A dense tangle of merge commits can be hard to read; an aggressively rebased or squashed history can obscure why decisions were made.
The mechanics of merging are consistent across setups — but how those mechanics interact with your team size, your branching conventions, your deployment process, and your tolerance for history complexity is where the real decisions live. Understanding the tools is the first step; figuring out which approach fits your specific project is where the actual thinking begins.