Merge

About

Merging in Git is the process of combining changes from two branches into one. It's used when we want to integrate work from a feature branch back into the main development branch (e.g., main, develop, or release/1.x) or vice versa.

Merging preserves the history of both branches and creates a new commit (called a merge commit) that ties them together.

How Merge Works in Git ?

When we run a git merge, Git looks for the common ancestor of the two branches and compares the changes made on each side from that point.

Example scenario:

  • main and feature-x both originated from commit A.

  • main has commit B.

  • feature-x has commits C and D.

When we merge feature-x into main, Git:

  • Identifies commit A as the common ancestor.

  • Applies the changes from commits C and D onto the current main.

  • Creates a new merge commit E with two parents: B and D.

The resulting history looks like this:

A---B---------E (main)
     \       /
      C---D (feature-x)

Types of Merges

1. Fast-Forward Merge

When it happens ?

A fast-forward merge occurs when the current branch has no new commits since it diverged from the branch being merged. Git doesn’t need to create a merge commit — it simply moves the pointer forward.

Before:

 A---B---C---D (feature)

       (main)
  • main is currently pointing to C

  • feature has advanced to D

  • main has no new commits after C, so Git can fast-forward it to D

After:

A---B---C---D (main, feature)
  • Git just advances the main pointer to D, the tip of the feature branch.

  • This results in a linear history.

  • No merge commit is created.

Command

git checkout main
git merge feature

When to use ?

  • Feature branch is fully ahead of main with no divergence.

  • We want clean, simple history.

2. No Fast-Forward Merge (--no-ff)

When it happens ?

We use this when we want to force Git to create a merge commit, even if a fast-forward is possible. This helps preserve the context of the feature branch.

Before:

 A---B---C---D (feature)

       (main)
  • main is at commit C

  • feature is ahead by one commit: D

After:

A---B---C-------E (main)
         \     /
          D---/  (feature)
  • E is a merge commit with parents: C and D

  • main now includes the full history of the feature

  • feature still exists (optional to delete)

Explanation

  • Forces a new commit E to indicate a merge took place.

  • The feature branch’s existence is preserved in history.

  • Useful for tracking what was merged and when.

Command

git checkout main
git merge --no-ff feature

When to use ?

  • We want to preserve the feature branch's identity.

  • Our team policy enforces explicit merge commits.

  • For audit or traceability reasons.

3. Three-Way Merge (Standard Merge with Divergence)

When it happens ?

Both the base branch and feature branch have diverged — i.e., each has commits the other doesn’t have.

Before:

      E---F (feature)
     /
A---B---C (main)
  • B is the common ancestor

  • main has commits: B → C

  • feature has commits: B → E → F

After:

      E---F
     /     \
A---B---C---G (main)

          (merge commit)
  • G is the merge commit with two parents:

    • One from main (C)

    • One from feature (F)

  • main now includes changes from both branches

  • History is non-linear, but traceable

Command

git checkout main
git merge feature

When to use ?

  • The branches have diverged.

  • We want a complete history of how changes were integrated.

  • Most common real-world merge scenario.

4. Octopus Merge

When it happens ?

Used when merging more than two branches at once.

Before:

      C (feature1)
     /
A---B
     \
      D (feature2)
       \
        E (feature3)
  • All three feature branches (feature1, feature2, feature3) diverged from a common base (B)

  • They have no conflicting changes

After:

       C  D  E
        \ | /
A---B-----F (main)

     (merge commit)
  • F is the octopus merge commit

  • Has multiple parents: C, D, E

  • main now contains all changes from feature1, feature2, and feature3

Command

git merge feature1 feature2

When to use ?

  • Automated merges with no conflicts.

  • Integrating multiple feature branches at once (e.g., for a release).

5. Merge with Conflicts

When it happens ?

A merge conflict occurs when two branches modify the same line in a file or delete/rename the same file differently.

Git cannot automatically decide whose change to keep, so it stops the merge and asks for manual intervention.

Before Merge:

Let’s say we have a file greeting.txt.

Content of greeting.txt on main:
Hello from Main!

Content of greeting.txt on feature:
Hello from Feature!

Branch Structure:

      C (feature - changes greeting.txt)
     /
A---B---D (main - also changes greeting.txt)
  • B is the common ancestor

  • Both main and feature changed the same line in greeting.txt in different ways

Attempting Merge:

git checkout main
git merge feature

Git will now stop and show a merge conflict:

✖ Conflict in greeting.txt:

<<<<<<< HEAD
Hello from Main!
=======
Hello from Feature!
>>>>>>> feature
  • HEAD is our current branch (main)

  • feature is the incoming branch

  • We must choose which change to keep (or both)

Resolving the Conflict:

We manually edit the file, for example:

Hello from Main and Feature!

Then:

git add greeting.txt
git commit

Git will now complete the merge by creating a merge commit.

After Merge:

      C
     /
A---B---D---E (main)
         \  /
          (merge commit with conflict resolved)
  • E is the merge commit

  • It has two parents: D (main) and C (feature)

  • greeting.txt now contains the manually resolved content

  • The merge commit (E in our example) is a new commit created on the current branch we are merging into (here, main).

  • Even though we manually resolve conflicts, the merge commit still becomes the new tip of main.

  • This merge commit has two parents:

    • The tip of our current branch before merge (D on main)

    • The tip of the branch we merged in (C on feature)

Merge Strategy Options

Git also offers internal merge strategies used behind the scenes:

Strategy
Description

recursive (default)

Standard three-way merge; handles most cases

resolve

Simple strategy, limited capabilities

ours

Keeps the current branch’s changes only

theirs

Keeps the incoming branch’s changes (used in rebasing with strategy options)

octopus

Used when merging more than two branches

Example:

git merge --strategy=recursive feature

Comparison

Type
Merge Commit Created
Linear History
Conflict Handling
Typical Use Case

Fast-Forward

No

Yes

No (no divergence)

Simple merges, clean linear history

No Fast-Forward

Yes

No

Yes

Preserve feature branch history

Three-Way Merge

Yes

No

Yes

Diverged branches, active collaboration

Octopus Merge

Yes (single commit)

No

No (fails on conflict)

Merge multiple branches at once

Merge with Conflicts

Yes

No

Yes (manual resolution)

When overlapping changes cause conflicts

When to Use Merge ?

Use merge when:

  • We want to preserve the full history of both branches.

  • Our team prefers non-destructive history.

  • We are working on long-running branches (e.g., release/1.x, develop, etc.).

  • We want to use GitLab Merge Requests or GitHub Pull Requests and show clear integration points.

Merge vs Rebase

Aspect
Merge
Rebase

History

Preserved, non-linear

Linearized, rewritten

Merge commit

Yes

No (unless explicitly created)

Conflict handling

All at once

One commit at a time

Use case

Collaborative workflows, history trace

Clean history, private feature branches

Practical Use Cases

1. Standard Feature Branch Merge into Main Branch

Problem: We completed a feature in a separate branch (feature/login). Now we want to bring that code into the main integration branch (develop or main), while preserving commit history.

Why Use Merge: Merging retains the complete branch history and clearly shows that a feature was developed independently and then integrated.

Command:

git checkout develop
git merge feature/login

Effect:

  • Creates a new merge commit.

  • Keeps all commits from feature/login.

  • Shows a clear branch-and-merge structure.

2. Bugfix Branch Merged Back Into Production

Problem: We created a hotfix branch (hotfix/issue-404) from main to quickly fix a production bug. Now the fix is done and tested.

Why Use Merge: We want the fix to be merged back while preserving the commit structure and making it traceable.

Command:

git checkout main
git merge hotfix/issue-404

Effect:

  • Keeps the hotfix history visible.

  • Avoids altering existing commits (unlike rebase).

3. Combining Two Active Feature Branches

Problem: Two related features are being developed in separate branches (feature/auth and feature/dashboard), and now we need to test them together locally before integrating into main.

Why Use Merge: We want to combine branches without changing their history. Merging is safe and non-destructive.

Command:

git checkout feature/auth
git merge feature/dashboard

Effect: We can test how both features behave together. If something breaks, history is untouched and reversible.

4. Bringing Develop Changes into Feature Branch

Problem: We are working on a long-lived branch (feature/analytics), and the develop branch has changed significantly (other features, refactoring, etc.). We need to sync.

Why Use Merge: We want to bring changes from develop into our branch to test compatibility and resolve conflicts without rewriting our feature’s history.

Command:

git checkout feature/analytics
git merge develop

Effect:

  • Resolves conflicts now instead of during the final merge.

  • Preserves our work’s history.

  • Keeps commits in their original sequence.

5. Merging for Collaboration Between Teammates

Problem: Two developers work on the same branch locally and need to sync up regularly without rebasing over each other’s history.

Why Use Merge: Merging is safe in shared branches. It doesn’t force history rewrites, which would create confusion or conflicts in shared repos.

Command:

git pull --no-rebase

(Performs a fetch + merge. Use --no-rebase to explicitly avoid rebasing.)

Effect:

  • Maintains all commit history.

  • Preserves collaborative workflows.

Last updated

Was this helpful?