SovranCode
HomeCourses Git & GitHub Git Merge
This device
Course contentsGit Merge · 41 topics

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
Learn Git & GitHub40 complete · 1 planned

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
PREVIOUS LESSONGit Branch
NEXT LESSONGit Merge Conflicts
3. Branches & History 70 min

Git Merge

Predict when Git can fast-forward, when it creates a merge commit, and how the resulting graph should be read.

What you will leave with

You will be able to identify the merge base, distinguish fast-forward from true merge commits, enforce --ff-only or --no-ff policy, read merge parents, abort an unclear integration, and prove feature commits remain reachable before branch cleanup.

Integration changes reachability

BaseFind the shared ancestor from which two branch histories diverged.
AdvanceRecognize when the current branch can move directly to a descendant tip.
CombineRead a merge commit as one snapshot with multiple parent histories.
VerifyTest the integrated tree and prove containment before deleting refs.

Merge direction starts with the current branch

git merge feature/search means “integrate feature/search into the branch currently checked out.” The current branch is the destination ref; the named branch is an input. Reversing checkout direction can produce a different branch movement and first-parent story.

DESTINATION

HEAD → main

Main is the branch Git may move. Its current tip becomes the first parent if a merge commit is created.

INPUT

feature/search

The named tip contributes commits and tree changes. The feature ref itself does not move because of this merge.

Prove the destination before merging

Run git branch --show-current. A correct feature name merged into the wrong current branch still creates the wrong integration history.

The merge base explains divergence

A merge base is a best common ancestor of two commits. It anchors feature-side comparison and tells you whether one tip is already an ancestor of the other.

git status
git branch --show-current
git log --oneline --decorate --graph --all
git merge-base main feature/search
git log main..feature/search --oneline
git log feature/search..main --oneline
git diff main...feature/search

Base equals main

Main’s tip is an ancestor of feature. A fast-forward is possible.

Base differs from both tips

Both sides have unique commits. Histories diverged and a normal merge needs a merge commit if it succeeds.

Base equals feature

Feature is already an ancestor of main. There may be nothing new to integrate.

No single assumption

Criss-cross histories can have multiple merge bases. Ordinary beginner graphs usually have one, but commands—not drawings from memory—decide.

QUICK CHECK

Test what you learned

Type the command that prints a best common ancestor of main and feature/search.

Fast-forward moves a ref to an existing commit

If main has not advanced since feature branched, the feature tip is a descendant of main. Git can integrate by moving main directly to the feature tip. No new commit object is needed.

  1. BEFORE · feature
    F2

    Add search empty state

  2. feature
    F1

    Add catalog search

  3. BEFORE · main
    B

    Shared base; main can move to F2.

git switch main
git merge --ff-only feature/search

git log --oneline --decorate --graph --all
git diff main...feature/search
git status

Afterward, main and feature/search both point to F2. The feature commits retain their IDs and parent relationships. The graph has no explicit integration commit.

--ff-only turns an expectation into policy

If you expect a linear ref movement, use the option. Git refuses when main has unique commits instead of silently creating a merge commit.

Diverged histories require integration

If main and feature both contain commits after their merge base, neither tip can simply replace the other without losing reachability from the destination branch. A successful normal merge creates a new snapshot with both tips as parents.

MAIN SIDE

B → M1

Main added release configuration after the branch point.

FEATURE SIDE

B → F1 → F2

Feature added search behavior from the same base.

The merge result does not concatenate commits or replay feature commits. Git performs a three-way tree merge using the merge base, current tip, and other tip, then records the integrated snapshot in a new commit if no unresolved conflict remains.

A merge commit records multiple parents

git switch main
git merge --no-ff feature/search

git show --no-patch --pretty=raw HEAD
git show --stat --first-parent HEAD
git log --oneline --decorate --graph --all
FIRST PARENT

Former main tip

The branch that was checked out when merge ran. First-parent history often reads as the integration timeline.

SECOND PARENT

Feature tip

The named branch tip integrated by the merge. Its complete ancestry remains reachable.

The merge commit stores a full project snapshot, metadata, message, and parent IDs. Its patch is comparison-dependent because there is more than one parent. Use git show --first-parent, git show -m, or combined diff views only after deciding which integration question you are asking.

Force an explicit merge event with no-ff

git merge --no-ff feature/search creates a merge commit even when main could fast-forward. Teams may choose this to preserve a visible feature boundary and a first-parent integration event.

Benefit

A named merge commit can group a feature’s commits and record when the branch entered the destination line.

Cost

More merge commits add graph structure and messages that must remain meaningful.

Not a correctness guarantee

--no-ff changes topology. It does not improve code, tests, review, or conflict resolution by itself.

Use project policy

Choose fast-forward, no-ff, squash, or review-platform strategies consistently rather than ad hoc aesthetics.

“Already up to date” is an ancestry result

If every commit reachable from the named branch is already reachable from the current branch, merge has no new history to integrate. Git reports the branch as up to date.

git merge-base --is-ancestor feature/search main
echo $?

# Exit 0 means feature/search is an ancestor of main.
git log main..feature/search --oneline
# Empty output confirms no feature-only reachable commits.

Shell exit-code display differs across shells; do not copy echo $? unchanged into PowerShell. The ancestry command itself is portable Git behavior.

Merge from a controlled working state

Begin with a clean index and working tree unless the project explicitly documents another workflow. Local changes can make the result harder to attribute or cause Git to refuse checkout updates.

  1. 01
    Confirm repository and destination

    Print the top-level path, current branch, and HEAD ID.

  2. 02
    Prove clean state

    Run status, plain diff, and cached diff. Account separately for untracked and ignored paths.

  3. 03
    Inspect ancestry and delta

    Read the graph, merge base, unique commit ranges, and three-dot diff.

  4. 04
    Choose topology policy

    Use normal merge, --ff-only, or --no-ff because the intended graph is explicit.

  5. 05
    Integrate and verify

    Inspect parents and snapshot, run tests, and prove final reachability.

If merge stops, do not improvise

A merge can stop because changes overlap, local state blocks an update, a hook fails, or another condition needs attention. Read the exact message and status. The next lesson covers conflict resolution in depth.

git status
git diff
git diff --cached

# If you should return to the pre-merge state:
git merge --abort
Do not commit conflict markers

If files contain <<<<<<<, =======, and >>>>>>>, integration is unresolved. Either resolve deliberately or abort before continuing.

Squash is not a merge commit

git merge --squash feature/search prepares the net feature change in the index and working tree but does not create a merge commit, move HEAD, or record the feature tip as a parent. A later ordinary commit has one parent.

TRUE MERGE

Preserves ancestry

Main’s history reaches the feature commits through the merge parent relationship.

SQUASH

Preserves net content only

Main receives a new single-parent commit with the combined patch; the feature commits are not ancestors of main.

Squash can fit a project’s review policy, but do not describe its result as “feature commits were merged” when the graph does not contain them.

Verify more than a successful command

git status
git log --oneline --decorate --graph --all
git branch --contains feature/search
git merge-base --is-ancestor feature/search main
git diff main...feature/search

# Run the repository's relevant tests, build, lint, or manual checks.

After a true merge or fast-forward, feature should be an ancestor of main and the three-dot diff is usually empty because their merge base is the feature tip. Verification must also cover behavior: a graph can integrate cleanly while the resulting application is wrong.

Delete the branch only after proof

Once integration, tests, status, and containment are correct, git branch -d feature/search removes the local feature name. It does not delete commits that remain reachable from main.

git branch --merged main
git log main..feature/search --oneline
git branch -d feature/search
git log --oneline --decorate --graph --all
Branch cleanup is not integration

Deleting a branch neither moves main nor copies content. Always merge and verify first; remove the name last.

Guided practice: produce both graph shapes

Use a disposable repository and begin with three commits on main.

  1. 01
    Create a fast-forward case

    Branch from main, add two feature commits, and prove main has no unique commits.

  2. 02
    Integrate with ff-only

    Predict the ref movement, merge, and prove no new commit object was created.

  3. 03
    Create divergence

    Make one new commit on main and two on another feature branch from the prior base.

  4. 04
    Create a merge commit

    Inspect both ranges and the feature patch, merge with no-ff, and identify both parent IDs.

  5. 05
    Verify and clean up

    Run checks, prove containment, delete both merged branch names with lowercase -d, and redraw the graph.

Independent lab: enforce and explain merge policy

Create a disposable repository that demonstrates all three policy outcomes:

  1. A feature integrated with --ff-only.
  2. A diverged feature where --ff-only refuses and leaves history unchanged.
  3. The same diverged feature integrated with an ordinary merge commit.
  4. A linear feature integrated with --no-ff to preserve an explicit integration event.
  5. A squash integration on a separate practice branch, with proof that feature commits are not ancestors of the destination.
  6. For each outcome, record merge base, directional ranges, graph before and after, parent count, resulting tree, and relevant tests.
  7. Finish with clean status and a written recommendation for one policy, including its review and history trade-offs.
Definition of done

You can predict every resulting graph before running merge, explain the actual parent relationships afterward, and distinguish content equivalence from ancestry preservation.

Common merge mistakes

Merging from the wrong branch

The current branch is the destination. Print it immediately before integration.

Assuming merge always creates a commit

Fast-forward moves a ref. Inspect ancestry and use an explicit policy option.

Reviewing only commit subjects

Inspect the three-dot patch and run behavioral checks before integration.

Calling squash a true merge

Squash transfers net content without parent ancestry from the feature branch.

Deleting before verification

Keep the feature ref until history, snapshot, tests, and containment all pass.

Equating no conflict with correctness

Git can combine text cleanly while producing invalid behavior. Test the integrated tree.

Lesson review

You can now predict integration from ancestry. Fast-forward moves the destination ref to an existing descendant; diverged histories need a merge commit to preserve both parent lines; policy flags make expected topology explicit; verification proves both reachability and behavior.

  • I identify the destination branch and merge base before merging.
  • I distinguish ref movement, true merge commits, and squash content transfer.
  • I use --ff-only or --no-ff when topology is part of the contract.
  • I can name the first and second parents of a two-branch merge commit.
  • I verify the graph, resulting snapshot, tests, and containment before branch deletion.

Related lessons

  • Git Branch — Merge direction starts from the current branch.
  • Git Merge Conflicts — Diverged edits can stop a merge until you resolve them.
KNOWLEDGE CHECK

Check your merge-topology model

Predict ref movement, parent relationships, and policy outcomes before integrating branches.

01When can Git fast-forward main to feature?
02What does a fast-forward merge create?
03What is true of an ordinary merge commit?
04What does git merge --ff-only feature do if histories diverged?
05Why run git diff main...feature before merging?
06What does git merge --no-ff feature do when a fast-forward is possible?
07When should a feature branch be deleted after merging?
PREVIOUS LESSONGit Branch
NEXT LESSONGit Merge Conflicts
ON THIS PAGEGit MergeIntegration changes reachabilityMerge direction starts with the current branchThe merge base explains divergenceFast-forward moves a ref to an existing commitDiverged histories require integrationA merge commit records multiple parentsForce an explicit merge event with no-ff“Already up to date” is an ancestry resultMerge from a controlled working stateIf merge stops, do not improviseSquash is not a merge commitVerify more than a successful commandDelete the branch only after proofGuided practice: produce both graph shapesIndependent lab: enforce and explain merge policyCommon merge mistakesLesson reviewKnowledge checkRelated lessons
Course contents