Git - Merging

Intermediate

Merging combines changes from different branches into one. It's how parallel development streams come together in Git, and understanding merging is essential for effective collaboration.

What is Merging?

Merging integrates changes from one branch into another. There are two main types:

Fast-forward merge (linear history):
Before merge:
main:     A---B---C
               \
feature:        D---E

After merge:
main:     A---B---C---D---E  ← main moves forward
Three-way merge (creates merge commit):
Before merge:
main:     A---B---C---F
               \
feature:        D---E

After merge:
main:     A---B---C---F---G  ← G is merge commit
               \         /
feature:        D---E---/

Basic Merge Commands

Simple Merge

Merge feature branch into current branch:
# Switch to target branch (usually main)
git switch main

# Merge feature branch
git merge feature-branch
Fast-forward merge output:
Updating a1b2c3d..b2c3d4e
Fast-forward
 feature.js | 25 +++++++++++++++++++++++++
 README.md  |  3 +++
 2 files changed, 28 insertions(+)
Three-way merge output:
Merge made by the 'ort' strategy.
 feature.js | 25 +++++++++++++++++++++++++
 README.md  |  3 +++
 2 files changed, 28 insertions(+)

Fast-Forward Merges

When the current branch hasn't diverged from the feature branch, Git can simply move the branch pointer forward:

Enabling/Disabling Fast-Forward

# Allow fast-forward (default)
git merge feature-branch

# Force merge commit even if fast-forward possible
git merge --no-ff feature-branch

# Only merge if fast-forward possible
git merge --ff-only feature-branch

When to Use --no-ff

Use --no-ff when you want to:
  • Preserve branch history visually
  • Group related commits together
  • Make it easy to revert entire features
  • Follow team merge policies

Three-Way Merges

When branches have diverged, Git performs a three-way merge using:

  • Base commit: Common ancestor of both branches
  • Ours: Current branch's latest commit
  • Theirs: Feature branch's latest commit

Merge Commit Messages

# Merge with custom message
git merge feature-branch -m "Merge feature: Add user authentication"

# Edit merge message in editor
git merge feature-branch  # Opens editor for message
Default merge commit message:
Merge branch 'feature-login' into main

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Merge Conflicts

Conflicts occur when the same lines are modified in both branches. Git cannot automatically resolve these:

Conflict Example

Merge conflict output:
Auto-merging config.js
CONFLICT (content): Merge conflict in config.js
Automatic merge failed; fix conflicts and then commit the result.
Check status during conflict:
git status
Status during conflict:
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add ..." to mark resolution)
	both modified:   config.js

no changes added to commit (use "git add" or "git commit -a")

Conflict Markers

Conflicted file content:
const config = {
  database: {
<<<<<<< HEAD
    host: 'localhost',
    port: 5432
=======
    host: 'production-db.example.com',
    port: 3306
>>>>>>> feature-database
  }
};

Conflict markers explained:

  • <<<<<<< HEAD: Start of current branch's version
  • =======: Separator between versions
  • >>>>>>> feature-database: End of merging branch's version

Resolving Conflicts

Manual resolution steps:
# 1. Edit the conflicted file
nano config.js  # Remove markers, choose/combine content

# 2. Stage the resolved file
git add config.js

# 3. Complete the merge
git commit  # Uses default merge message
Example resolution:
const config = {
  database: {
    host: process.env.NODE_ENV === 'production' 
      ? 'production-db.example.com' 
      : 'localhost',
    port: process.env.NODE_ENV === 'production' ? 3306 : 5432
  }
};

Merge Tools

Built-in Merge Tool

# Launch merge tool
git mergetool

Configure External Tools

# Configure VS Code as merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# Configure vimdiff
git config --global merge.tool vimdiff

# Configure Beyond Compare
git config --global merge.tool bc3
git config --global mergetool.bc3.cmd "bcompare \$LOCAL \$REMOTE \$BASE \$MERGED"

Aborting Merges

# Abort merge and return to pre-merge state
git merge --abort

# Reset to before merge (more drastic)
git reset --hard HEAD~1
git reset --hard will lose uncommitted changes. Use git merge --abort when possible.

Advanced Merge Strategies

Merge Strategies

# Use specific merge strategy
git merge -s ort feature-branch      # Default strategy
git merge -s resolve feature-branch  # Three-way merge only
git merge -s octopus branch1 branch2 branch3  # Multiple branches

Strategy Options

# Favor our changes during conflicts
git merge -X ours feature-branch

# Favor their changes during conflicts  
git merge -X theirs feature-branch

# Ignore whitespace changes
git merge -X ignore-space-change feature-branch

Squash Merging

Squash merging combines all commits from the feature branch into a single commit:

# Squash merge (doesn't create merge commit)
git merge --squash feature-branch

# Review changes
git diff --staged

# Commit the squashed changes
git commit -m "Add user authentication feature

- Add login form component
- Implement password validation  
- Add session management
- Update user interface"
Before squash merge:
main:     A---B---C
               \
feature:        D---E---F---G
After squash merge:
main:     A---B---C---H  ← H contains all changes from D,E,F,G
               \
feature:        D---E---F---G  ← Branch still exists

Merge Workflows

Feature Branch Workflow

Complete feature merge process:
# 1. Update main branch
git switch main
git pull origin main

# 2. Switch to feature branch
git switch feature-user-auth

# 3. Rebase on latest main (optional)
git rebase main

# 4. Switch back to main
git switch main

# 5. Merge feature
git merge --no-ff feature-user-auth

# 6. Push merged changes
git push origin main

# 7. Delete feature branch
git branch -d feature-user-auth
git push origin --delete feature-user-auth

Release Branch Workflow

# Merge release branch to main
git switch main
git merge --no-ff release/v1.2.0

# Tag the release
git tag -a v1.2.0 -m "Release version 1.2.0"

# Merge back to develop
git switch develop  
git merge --no-ff release/v1.2.0

# Clean up
git branch -d release/v1.2.0

Merge History and Analysis

Viewing Merge History

# Show merge commits only
git log --merges --oneline

# Show first parent only (main line of development)
git log --first-parent --oneline

# Show graph with merge visualization
git log --graph --oneline --all

Finding Merge Information

# Show what branches were merged
git show --format=fuller merge-commit-hash

# Find merge that introduced a commit
git log --merges --ancestry-path commit-hash..HEAD

# Show merge conflicts that were resolved
git show merge-commit-hash

Troubleshooting Merge Issues

Large Number of Conflicts

# See all conflicted files
git diff --name-only --diff-filter=U

# Check merge progress
git status

# Use merge tool for each conflict
git mergetool

Merge Commit Problems

# Undo last merge (if not pushed)
git reset --hard HEAD~1

# Revert merge commit (safe for pushed changes)
git revert -m 1 merge-commit-hash

Best Practices

Before Merging

  • ✅ Update your main branch
  • ✅ Test your feature branch thoroughly
  • ✅ Consider rebasing feature branch on main
  • ✅ Review all changes one final time

Merge Strategies by Project Type

Open source projects:
# Preserve contribution history
git merge --no-ff contributor-branch
Clean history preference:
# Squash small features
git merge --squash small-feature

# Regular merge for significant features
git merge --no-ff major-feature

Merge Configuration

# Default merge behavior
git config --global merge.ff false  # Always create merge commits

# Default merge tool
git config --global merge.tool vscode

# Keep backup files from merge tool
git config --global mergetool.keepBackup false

# Show common ancestor in conflict markers
git config --global merge.conflictstyle diff3

Summary

You now understand Git merging:

  • ✅ Fast-forward vs three-way merges
  • ✅ Merge conflicts and resolution techniques
  • ✅ Merge tools for easier conflict resolution
  • ✅ Squash merging for clean history
  • ✅ Different merge strategies and options
  • ✅ Workflow patterns for different scenarios
  • ✅ Troubleshooting and undoing merges

Next Steps

Now that you understand merging, let's explore more advanced branching techniques:

Git - Advanced Branching

Practice Tip: Create a repository with conflicting changes on different branches and practice resolving merge conflicts. This hands-on experience is invaluable.