Skip to content

Merging

Merging is the process of combining the changes from one branch into another. Git’s merge algorithm Is one of its most sophisticated features — it can automatically resolve many cases where both Branches have modified different files or different parts of the same file.

A merge takes two (or more) commit pointers — branch tips — and produces a new merge Commit that has both as parents:

Terminal window
$ git switch main
$ git merge feature-auth
gitGraph
    commit id: "C (base)"
    checkout main
    commit id: "D (ours)"
    checkout feature-auth
    commit id: "E (theirs)"
    checkout main
    merge feature-auth id: "F (merge commit)"

The merge commit F has two parents: D (main’s previous position) and E (feature-auth’s tip). Its tree is the result of combining the changes from both branches.

Git uses the three-way merge algorithm, which requires three snapshots:

  1. Base (common ancestor): The most recent commit that is an ancestor of both branches.
  2. Ours: The tip of the branch you are merging into (the current branch).
  3. Theirs: The tip of the branch you are merging from.
flowchart TD
    BASE["Base commit<br/>(common ancestor)"]
    OURS["Ours<br/>(current branch)"]
    THEIRS["Theirs<br/>(branch to merge)"]
    RESULT["Merge result"]

    BASE -->|"What changed?"| OURS
    BASE -->|"What changed?"| THEIRS
    OURS -->|"Our changes"| RESULT
    THEIRS -->|"Their changes"| RESULT

    style BASE fill:#fff3e0
    style OURS fill:#e8f5e9
    style THEIRS fill:#e3f2fd
    style RESULT fill:#f3e5f5

The algorithm works file by file, hunk by hunk:

BaseOursTheirsResultExplanation
xxxxNo change — keep as-is
xyxyOnly we changed — take ours
xxyyOnly they changed — take theirs
xyzConflictBoth changed differently — manual resolution required

The critical case is the last row: when both branches modify the same region of the same file. This Is a merge conflict.

Git finds the base commit by computing the lowest common ancestor (LCA) of the two branch tips In the commit DAG. This is not trivial when the history contains multiple merge bases (criss-cross Merges):

gitGraph
    commit id: "A"
    branch left
    checkout left
    commit id: "B"
    checkout main
    commit id: "C"
    merge left id: "D"
    checkout left
    merge main id: "E"
    commit id: "F"
    checkout main
    commit id: "G"

In criss-cross situations like this, there are two possible merge bases (B and C). Git’s Default recursive strategy recursively merges these bases first to create a virtual base, then Performs the three-way merge against it.

When the current branch has no new commits since the branch point (i.e., the current branch is an Ancestor of the branch being merged), Git can perform a fast-forward merge. This moves The branch pointer forward — no merge commit is created.

gitGraph
    commit id: "A"
    commit id: "B"
    checkout feature
    commit id: "C"
    commit id: "D"
    checkout main
    merge feature id: "D (fast-forward)"
Terminal window
$ git switch main
$ git merge feature-auth
Updating a3f2b1c..b7e9d4f
Fast-forward
src/auth.c | 42 +++++++++++++++
1 file changed, 42 insertions(+)

Sometimes you want a merge commit even when a fast-forward is possible — to preserve a record of the Merge event:

Terminal window
$ git merge --no-ff feature-auth
gitGraph
    commit id: "A"
    commit id: "B"
    checkout feature
    commit id: "C"
    commit id: "D"
    checkout main
    merge feature id: "E (--no-ff)" noff

This is common in release workflows where the merge commit serves as a “release marker” that can be identified in the history.

Merging combines two branches’ histories into one. A fast-forward merge is a simple pointer move when the target branch has no new commits. A three-way merge creates a new commit that combines both branches, with a common ancestor as the reference. Conflicts happen when both branches modify the same lines: Git asks you to choose which version to keep. Think of merging as reconciling two drafts of a document: you keep the best parts from each and resolve any contradictions.