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.
Main points to C. feature/login started at C and added D → E. Main has not moved since the branch was created. If you switch to main and run git merge feature/login, must Git create a new merge commit?
Reveal the reasoning
No. Because main is still an ancestor of feature/login, Git can fast-forward main directly to E. There is no divergent history to reconcile, so a two-parent merge commit is unnecessary unless you explicitly request one with --no-ff.
Merge updates the branch you currently have checked out. Before running it, say the direction out loud: “I want branch X to receive branch Y.” Then switch to X and merge Y.
Before you run it
- Run git status and confirm the branch that should receive the other line of work is the current branch.
- Inspect git log --graph --oneline --decorate --all and decide whether the result should be a fast-forward or a true integration commit.
- Fetch first when you intend to merge a remote-tracking branch so the target reflects the remote state you actually inspected.
- Decide whether preserving branch topology has project value; do not use --no-ff or --squash as unexplained style preferences.
- 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.
Options and the decision behind them
--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
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 output:
Updating <old>..<new>
Fast-forward
… or a merge commit message when histories have diverged. 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.
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 output:
Fast-forward on success; fatal: Not possible to fast-forward, aborting. when the histories diverged. When merge pauses, protect the integrated behavior—not one side of the conflict
A merge conflict means Git cannot automatically combine the two branch tips. “Ours” and “theirs” describe graph positions, not which business behavior is correct.
Confirm the receiving branch
Check repository state first so you know which branch is being updated and which paths are unresolved.
git statusInspect both sides
Read the conflict in context and compare each branch’s intent before choosing or combining changes.
git diff --ours git diff --theirsBuild the intended combined result
Edit the files so both valid behaviors survive where appropriate, then run focused tests rather than merely deleting conflict markers.
Stage and complete
Stage resolved files and complete the merge only after the integrated behavior is verified.
git add <resolved-files> git commitAbort when the direction or plan was wrong
If you merged from the wrong branch or the conflict set reveals a bad integration plan, return to the pre-merge state.
git merge --abort
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
A shared feature branch already contains commits your teammates pulled. origin/main has advanced. You want the feature branch to include the new main work without replacing the published feature commit IDs. What sequence fits that collaboration constraint?
Hint
Update the remote-tracking reference first, then integrate the remote main line into the shared feature branch without rewriting existing feature commits.
Tutor answer
git switch feature/payments
git fetch origin
git merge origin/mainThe feature branch is the receiver, and a normal merge preserves the commit identities teammates already have. If the histories diverged, Git records the integration point instead of replaying published feature commits.
Independent practice
Create a disposable repository where one branch can fast-forward and another has truly diverged. Predict the graph before each merge, run the operations, compare fast-forward versus merge-commit results, then deliberately create one conflict and practice both resolving it and aborting it.
Related commands to keep nearby
git statusgit log --graph --oneline --decorategit fetchgit diffgit rebaseKeep the working set small enough that each command has a clear job: inspect state, update remote knowledge, compare changes, integrate history, or recover from mistakes.
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.