Git command reference · Reviewed 2026-08-16

git merge: understand the history before you rewrite it

Learn git merge through commit-graph reasoning, fast-forward behavior, merge commits, conflict recovery, and safe collaboration scenarios.

git merge [options] <commit>…

Start with the mental model

Merge integrates another line of Git history into the branch you currently have checked out. Unlike rebase, it normally preserves the existing commit identities and the fact that independent branch histories existed.

Predict before reading on

You have commits A → B → C on main. Your feature branch starts at B and adds D → E. If you rebase the feature onto C, do you still have the exact same D and E commits?

Reveal the reasoning

No. Git replays the changes from D and E after C, producing new commits—think D′ and E′—because their parent chain changed. The file changes may be equivalent, but the commit identities are not.

Before you use it

  • Know that the branch you currently have checked out is the branch Git will move or update during the merge.
  • Understand that commits form a graph through parent links, and two branches can point to different tips in that graph.
  • Be comfortable checking repository state with git status and inspecting history with git log.

A useful safety habit is to run git status and inspect a compact graph before a rebase. If you cannot explain which commits are local and which base you intend to move onto, stop there and inspect first.

Common options and what they really mean

--ff-only

Allow the merge only when the current branch can move directly to the target without creating a merge commit.

Use it when: Automation or protected workflows where an unexpected divergent history should fail instead of being merged implicitly.
--no-ff

Create a merge commit even when Git could fast-forward.

Use it when: When preserving the existence and integration point of a feature or release branch is intentionally valuable to the project history.
--squash

Apply the combined changes from the other branch to the working tree and index without creating the branch topology as a merge commit.

Use it when: When you deliberately want one new commit containing the net change and do not need the source branch commits connected into the current history.
--abort

Stop a conflicted merge and try to restore the state from before the merge began.

Use it when: When the conflict set is unexpected, the wrong branch was selected, or you need to reconsider the integration plan.

Worked examples

Example 1

Beginner: merge a completed feature into main

Situation: Your feature/login branch is complete and tested. You want to integrate it into local main while preserving the existing feature commits.

git switch main
git merge feature/login

The important first step is switching to the branch that should receive the work. Git then compares the two branch tips. If main has not advanced since the feature branched, Git may fast-forward; if both histories diverged, a normal merge usually creates a new commit with two parents.

Typical success output:

Updating <old>..<new>
Fast-forward
… or a merge commit message when histories have diverged.
Example 2

Practical: update a shared feature branch from main without rewriting teammates’ commits

Situation: Several developers have already pulled feature/payments. Main has moved forward, and the team wants the feature branch to include those changes without replacing published commit IDs.

git switch feature/payments
git fetch origin
git merge origin/main

Fetching first updates the remote-tracking main reference. Merging origin/main into the shared feature branch preserves the commits everyone already has while recording the point where the newer main history was integrated.

Example 3

Advanced: require a true fast-forward in automation

Situation: A deployment branch is expected to be strictly behind main. If the histories diverged, you want the job to fail so a human can inspect why.

git switch deployment
git fetch origin
git merge --ff-only origin/main

With --ff-only, Git refuses to invent an integration commit. Success proves the deployment branch tip was an ancestor of origin/main and could simply move forward. A failure is useful evidence that the branch history no longer matches the workflow assumption.

Typical success output:

Fast-forward on success; fatal: Not possible to fast-forward, aborting. when the histories diverged.

Conflict recovery: slow down instead of guessing

When a replayed commit does not apply cleanly, Git pauses. That pause is useful: it tells you the old change and the new base disagree about the same area. Resolve the intent, not merely the conflict markers.

  1. Run git status and identify the files Git says are conflicted.
  2. Read the surrounding code and determine what the replayed commit was trying to accomplish.
  3. Edit the files so the result makes sense on the new base, then run relevant tests or checks.
  4. Stage resolved files with git add.
  5. Run git rebase --continue. If the entire approach is wrong, use git rebase --abort instead.

When this command goes wrong

Common mistakes and why they fail

Merging while checked out on the wrong branch

git merge updates the current branch. If you intended to put feature work into main but are still on the feature branch, the direction of integration is reversed.

Safer response: Before merging, run git status or inspect the prompt and state the goal in words: “I want branch X to receive branch Y.” Switch to X, then merge Y.

Assuming every merge creates a merge commit

When the current tip is an ancestor of the target, Git can fast-forward by moving the branch pointer. No new commit is required because there is no divergent history to reconcile.

Safer response: Inspect git log --graph --oneline --decorate before merging. Use --no-ff only when preserving an explicit integration point is a deliberate project choice.

Resolving a textual conflict by picking one side wholesale

Conflict markers identify overlapping edits, not which side is correct. “Ours” and “theirs” are repository positions, not business-intent labels.

Safer response: Read the surrounding code, understand both changes, create the intended combined result, stage it, run focused tests, then complete the merge.

Using --squash without understanding the history trade-off

A squash merge copies the net changes into the index but does not connect the source branch commits as parents of the resulting commit. The content can be correct while the graph tells a different story.

Safer response: Choose squash because the project wants a single integration commit, not merely because the graph looks shorter. Use normal merge when branch ancestry is useful history.

Guided practice

Your branch has three local commits and origin/main has moved forward. You have not pushed your branch. What sequence gives you the latest remote base while keeping your local commits linear?

Hint

First update your knowledge of the remote without changing your branch. Then replay the local commits onto that remote-tracking branch.

Tutor answer
git fetch origin
git rebase origin/main

The important reasoning is that git fetch updates origin/main; it does not silently merge it. The rebase step then has an explicit, inspectable target.

Independent practice

Create a disposable repository with a main branch and a feature branch. Make main advance after the branch point, then make two feature commits. Draw the commit graph before rebasing, predict the graph after rebasing, run the rebase, and compare your prediction with git log --graph --oneline --decorate --all. Finally, inspect git reflog to find the feature branch’s previous tip.

Related commands

  • git status
  • git log --graph --oneline --decorate
  • git fetch
  • git diff
  • git rebase

These are intentionally listed as a small working set rather than a link dump. Status and graph inspection help you understand state; fetch updates remote knowledge; reflog helps recovery; merge is the main alternative when preserving shared history matters.

Frequently asked questions

What is a fast-forward merge?

If the current branch tip is already an ancestor of the branch being merged, Git does not need to combine divergent lines. It can simply move the current branch pointer forward to the newer commit.

When should I use --no-ff?

Use it when the project deliberately wants an explicit merge commit to preserve the fact that a feature, release, or other branch was integrated as a unit. Do not use it automatically if the extra topology provides no value.

Is merge safer than rebase?

For already-shared history, merge is often safer because it normally preserves existing commit IDs. That does not make every merge correct: you still need to choose the right direction, resolve conflicts deliberately, and test the integrated result.

How do I cancel a merge conflict?

If the merge is still in progress and you have not intentionally completed it, git merge --abort is the normal escape route. Inspect git status first so you understand the current state, especially if you had unrelated uncommitted work before merging.

Next step

Practice on disposable history before using rebase under pressure.

The safest way to become comfortable with rebasing is to predict commit graphs, perform the operation, inspect the result, and deliberately practice abort/recovery in a repository where nothing important can be lost.

Explore command references