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.
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-onlyAllow 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-ffCreate 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.--squashApply 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.--abortStop 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
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. 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.
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.
- Run
git statusand identify the files Git says are conflicted. - Read the surrounding code and determine what the replayed commit was trying to accomplish.
- Edit the files so the result makes sense on the new base, then run relevant tests or checks.
- Stage resolved files with
git add. - Run
git rebase --continue. If the entire approach is wrong, usegit rebase --abortinstead.
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/mainThe 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 statusgit log --graph --oneline --decorategit fetchgit diffgit 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.