Merging is how you integrate changes from one branch into another. It's the process of bringing your feature work back into the main codebase, combining bug fixes, or integrating contributions from team members. In this lesson, you'll learn the different types of merges Git performs, understand when each happens, and master the workflow of integrating branches. You'll also get your first look at merge conflicts (though we'll dive deeper into resolving them in the next lesson).
What Is Merging?
Merging is the process of combining the changes from one branch into another. When you merge, Git takes the commits from the source branch and integrates them into the target branch.
Important terminology:
- Target branch: The branch you're merging INTO (where you currently are)
- Source branch: The branch you're merging FROM (the branch being merged)
Example: If you're on main and run git merge feature/login, then main is the target and feature/login is the source.
Why Merge?
Remember why we create branches? To work on features, fixes, or experiments in isolation. Once that work is complete and tested, we need to bring it back into the main branch so it becomes part of the project. That's what merging does!
Common merge scenarios:
- Merging a completed feature branch into main
- Merging a bug fix into the production branch
- Merging main into your feature branch to get the latest updates
- Integrating changes from other developers
Types of Merges
Git performs different types of merges depending on the relationship between branches. Understanding these helps you predict what Git will do.
Fast-Forward Merge
A fast-forward merge is the simplest type. It happens when there's a direct, linear path from your current branch to the branch you're merging.
Imagine this scenario:
Before merge:
A --- B --- C (main)
\
D --- E (feature)
After fast-forward merge:
A --- B --- C --- D --- E (main, feature)Since main hasn't changed since the branch was created, Git simply moves the main pointer forward to point to commit E. No merge commit is created because no actual merging is needed—it's just moving the pointer forward!
When fast-forward happens:
- You created a branch from main
- Made commits on that branch
- Meanwhile, nobody made any commits on main
- You merge the branch back to main
Result: Main's pointer just "fast-forwards" to catch up!
Three-Way Merge
A three-way merge happens when both branches have diverged—when new commits exist on both branches since they split.
Imagine this scenario:
Before merge:
A --- B --- C --- F --- G (main)
\
D --- E (feature)
After three-way merge:
A --- B --- C --- F --- G --- H (main)
\ /
D ----------- E (feature)Git needs to create a new merge commit (H) that combines the changes from both branches. This is called a "three-way" merge because Git looks at three commits:
- The common ancestor (C) - where the branches split
- The target branch tip (G) - latest commit on main
- The source branch tip (E) - latest commit on feature
Git compares all three to intelligently determine what changed and creates a merge commit that includes changes from both branches.
The merge commit is special:
Unlike regular commits which have one parent, a merge commit has two parents—one from each branch. This preserves the history of both branches and shows where they came together.
Performing a Merge
Let's walk through a complete merge operation step by step.
Step 1: Prepare Your Branches
Before merging, make sure:
- Your work is committed: No uncommitted changes
git status
# Should show: nothing to commit, working tree clean- You're on the target branch: The branch you want to merge INTO
# If merging feature into main, switch to main
git switch main
# Verify you're on the right branch
git branch
# Should show: * main⚠️ Critical Rule
Always switch to the target branch first!
If you want to merge feature into main:
- ✅ Correct: Switch to
main, thengit merge feature - ❌ Wrong: Stay on
featureand mergemain
Step 2: Perform the Merge
git merge feature/loginGit will do one of the following:
Scenario A: Fast-Forward Merge
Updating a1b2c3d..f4e5d6c
Fast-forward
login.html | 45 +++++++++++++++++++++++++++++++++
auth.js | 23 ++++++++++++++++
2 files changed, 68 insertions(+)Success! Git performed a fast-forward merge. The changes are now in your current branch.
Scenario B: Three-Way Merge (No Conflicts)
Merge made by the 'recursive' strategy.
login.html | 45 +++++++++++++++++++++++++++++++++
auth.js | 23 ++++++++++++++++
2 files changed, 68 insertions(+)Git created a merge commit. Your default editor might open asking for a merge commit message (usually pre-filled with "Merge branch 'feature/login'").
Scenario C: Merge Conflict
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.Git found conflicts it can't resolve automatically. We'll learn how to handle this in the next lesson!
Step 3: Verify the Merge
# Check that merge completed
git status
# View the merge commit
git log --oneline -5
# See the merge visually
git log --oneline --graph --allPractical Merge Example
Let's walk through a complete real-world example from start to finish.
Scenario: Adding a Contact Form
- Start from main branch:
git switch main
git status # Verify clean working directory- Create a feature branch:
git switch -c feature/contact-form- Make changes and commit:
# Create contact.html
echo "Contact form content" > contact.html
git add contact.html
git commit -m "Add contact form page"
# Add styling
echo "Contact styles" > contact.css
git add contact.css
git commit -m "Add contact form styling"
# Add validation
echo "Validation code" > validate.js
git add validate.js
git commit -m "Add form validation"- View your branch history:
git log --oneline
# Output shows:
# f4e5d6c (HEAD -> feature/contact-form) Add form validation
# c7d8e9f Add contact form styling
# b0a1c2d Add contact form page
# a1b2c3d (main) Previous main commit- Switch back to main:
git switch main
# Notice the contact files are gone!
ls contact.* # Files not found
# Main hasn't changed
git log --oneline -1
# Still shows: a1b2c3d Previous main commit- Merge the feature branch:
git merge feature/contact-form
# Output:
# Updating a1b2c3d..f4e5d6c
# Fast-forward
# contact.html | 1 +
# contact.css | 1 +
# validate.js | 1 +
# 3 files changed, 3 insertions(+)- Verify the merge:
# Files are now in main
ls contact.*
# Output: contact.html contact.css
# Main now includes all the commits
git log --oneline
# f4e5d6c (HEAD -> main, feature/contact-form) Add form validation
# c7d8e9f Add contact form styling
# b0a1c2d Add contact form page
# a1b2c3d Previous main commit- Clean up - delete the feature branch:
git branch -d feature/contact-form
# Output:
# Deleted branch feature/contact-form (was f4e5d6c)Complete! The contact form feature is now part of main, and the feature branch has been cleaned up. This is the standard feature branch workflow!
Merge Commit Messages
When Git creates a merge commit (three-way merge), it opens your default editor with a pre-filled message:
Merge branch 'feature/contact-form'
# 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.You can:
- Keep the default message: Usually fine for simple merges
- Add details: Explain what the feature does or why it's being merged
- Reference issues: Link to issue trackers (e.g., "Fixes #123")
Example of a detailed merge commit message:
Merge branch 'feature/user-authentication'
Add JWT-based authentication system with the following features:
- User registration and login
- Password hashing with bcrypt
- JWT token generation and validation
- Protected route middleware
- Session management
Closes #45💡 Merge Message on Command Line
You can provide the merge commit message directly without opening an editor:
git merge feature/auth -m "Merge authentication feature - closes #45"Forcing a Merge Commit
Sometimes you want to create a merge commit even when a fast-forward is possible. This preserves the history of your feature branch.
git merge --no-ff feature/loginThe --no-ff flag (no fast-forward) forces Git to create a merge commit even when it could fast-forward.
Why would you want this?
- Preserve feature history: Keep a record that a feature branch existed
- Better for code review: Easy to see what commits were part of a feature
- Easier to revert: Can revert entire features in one command
- Team workflows: Many teams always use
--no-fffor consistency
Compare the history:
With fast-forward (--ff):
A --- B --- C --- D --- E (main)
(feature commits are now part of main's linear history)
With no-fast-forward (--no-ff):
A --- B --- C ------- M (main)
\ /
D - E (merge commit M preserves that D-E were a feature)Aborting a Merge
If you start a merge and realize it's not the right time (maybe you need to make changes first), you can abort it:
git merge --abortThis returns your repository to the state it was in before you ran git merge. Very useful when you encounter unexpected conflicts or realize you're on the wrong branch!
When to abort:
- You merged the wrong branch
- You're on the wrong target branch
- Too many conflicts and you want to prepare better
- You realize you need to make changes first
Understanding Merge Strategies
Git uses different strategies to perform merges. Most of the time, Git chooses the right one automatically.
Recursive Strategy (Default)
Used for three-way merges with two branches. This is Git's default and handles most situations well, including complex merges with renames.
Ours and Theirs
When conflicts occur, you can choose to always favor one side:
# Merge but always favor current branch in conflicts
git merge -X ours feature/branch
# Merge but always favor incoming branch in conflicts
git merge -X theirs feature/branchUse with caution! These strategies automatically resolve conflicts by choosing one side. This can discard important changes. Only use when you're certain which side should win.
Octopus Strategy
Used when merging more than two branches at once (rare in daily development):
git merge branch1 branch2 branch3Viewing Merge History
Several commands help you understand your merge history:
Visual Graph
git log --oneline --graph --all* a1b2c3d (HEAD -> main) Merge branch 'feature/login'
|\
| * f4e5d6c (feature/login) Add authentication
| * c7d8e9f Create login form
|/
* b0a1c2d Update homepage
* e1f2a3b Initial commitSee Only Merge Commits
git log --merges --onelineSee Which Branches Were Merged
# Branches already merged into current branch
git branch --merged
# Branches not yet merged
git branch --no-mergedShow What a Merge Changed
# See what changed in a merge commit
git show a1b2c3d
# Compare before and after a merge
git diff a1b2c3d^ a1b2c3dIntroduction to Merge Conflicts
Sometimes Git can't automatically merge changes because the same lines were modified in both branches. This is called a merge conflict.
When Conflicts Occur
Conflicts happen when:
- The same line(s) were changed differently in both branches
- One branch modified a file that the other branch deleted
- Both branches added a file with the same name but different content
What Git Does
When a conflict occurs, Git:
- Pauses the merge process
- Marks the conflicted files
- Adds conflict markers to show both versions
- Asks you to resolve the conflict manually
Identifying Conflicts
git merge feature/updates
# Output:
# Auto-merging index.html
# CONFLICT (content): Merge conflict in index.html
# Automatic merge failed; fix conflicts and then commit the result.git status
# Output:
# On branch main
# You have unmerged paths.
# (fix conflicts and run "git commit")
#
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# both modified: index.htmlConflict Markers
Git adds special markers to the conflicted file:
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
</head>
<body>
<<<<<<< HEAD
<h1>Welcome to My Website</h1>
<p>Main branch version</p>
=======
<h1>Welcome to Our Site</h1>
<p>Feature branch version</p>
>>>>>>> feature/updates
</body>
</html><<<<<<< HEAD- Start of your current branch's version=======- Separator between the two versions>>>>>>> feature/updates- End of the incoming branch's version
Don't worry! We'll learn how to resolve conflicts in detail in the next lesson. For now, just know that conflicts are normal and solvable!
Quick Conflict Resolution Preview
The basic process (detailed in the next lesson):
- Open the conflicted file
- Find the conflict markers
- Decide which version to keep (or combine both)
- Remove the conflict markers
- Stage the resolved file with
git add - Complete the merge with
git commit
Try the conflict resolver below to get a feel for the process:
Practice Resolving Conflicts
Try resolving a simple merge conflict
Merge conflict in app.js
Automatic merge failed. Fix conflicts and commit the result.
Conflict Markers
<<<<<<< main
function greeting() {
console.log("Hello, World!");
return "Welcome!";
}
=======
function greeting() {
console.log("Hello, Developer!");
return "Welcome to our app!";
}
>>>>>>> feature/new-uiCurrent (main)
function greeting() {
console.log("Hello, World!");
return "Welcome!";
}Incoming (feature/new-ui)
function greeting() {
console.log("Hello, Developer!");
return "Welcome to our app!";
}Your Resolution
Visualize Merging
Use this interactive tool to experiment with creating branches and merging them:
Experiment with Branch Merging
Create branches, make commits, and merge them together
Commit Log
Add features
main • 11:30 AM
Initial commit
main • 10:00 AM
Merge Best Practices
1. Keep Main Branch Clean
Only merge tested, working code into main. Think of main as your production-ready code.
2. Merge Often
Don't let feature branches live for weeks. Merge frequently to avoid large, complex merges with many conflicts.
3. Update Before Merging
Before merging your feature branch, update it with the latest from main:
# On your feature branch
git switch feature/login
# Get latest main changes
git merge main
# Resolve any conflicts now
# Then switch to main and merge your feature
git switch main
git merge feature/login4. Test Before Merging
Always test your changes before merging. A good workflow:
- Complete feature on branch
- Test thoroughly
- Merge main into your branch (get latest changes)
- Test again
- Merge your branch into main
5. Write Good Merge Commit Messages
For important merges, write descriptive messages explaining what the feature does, not just "Merge branch X".
6. Delete Merged Branches
After successfully merging and verifying, delete the feature branch:
git branch -d feature/login7. Use Pull Requests (on GitHub)
When working with teams, use GitHub Pull Requests instead of merging directly. They provide code review and discussion (we'll learn this later).
Common Merge Workflows
Feature Integration Workflow
# 1. Create feature branch
git switch main
git switch -c feature/notifications
# 2. Develop feature (multiple commits)
git add .
git commit -m "Add notification system"
# ... more commits ...
# 3. Update with latest main
git switch main
git pull # Get latest if working with others
git switch feature/notifications
git merge main # Integrate latest changes
# 4. Test everything works
# 5. Merge into main
git switch main
git merge feature/notifications
# 6. Push to remote (if applicable)
git push
# 7. Delete feature branch
git branch -d feature/notificationsHotfix Workflow
# Emergency bug in production!
# 1. Create hotfix from main
git switch main
git switch -c hotfix/critical-bug
# 2. Fix the bug quickly
git add .
git commit -m "Fix critical security vulnerability"
# 3. Merge to main
git switch main
git merge hotfix/critical-bug
# 4. Deploy to production
git push
# 5. Merge hotfix to active development branches
git switch develop
git merge hotfix/critical-bug
# 6. Delete hotfix branch
git branch -d hotfix/critical-bugPractice Merge Commands
Try these commands to practice merging:
Practice Git Merge
Experiment with merge commands
Try these examples:
Troubleshooting Merges
Issue: Merged the Wrong Branch
Problem: You merged the wrong branch into main
Solution: Undo the merge:
# Undo the last merge (if it was the last commit)
git reset --hard HEAD~1
# Or if you've already pushed
git revert -m 1 HEADIssue: Merge Conflicts Everywhere
Problem: Too many conflicts to deal with right now
Solution: Abort and prepare better:
git merge --abort
# Then:
# - Review what changed in both branches
# - Consider smaller, incremental merges
# - Coordinate with your teamIssue: Forgot to Switch Branches
Problem: You merged while on the wrong branch
Solution: Reset and try again:
# Undo the merge
git reset --hard HEAD~1
# Switch to correct branch
git switch main
# Merge properly
git merge feature/branchCommand Reference
Here's a quick reference of all merge commands:
# Basic Merging
git merge branch-name # Merge branch into current branch
git merge --no-ff branch-name # Force merge commit (no fast-forward)
git merge --abort # Cancel merge and return to pre-merge state
# Merge with Message
git merge branch -m "message" # Provide merge commit message
# Merge Strategies
git merge -X ours branch # Favor current branch in conflicts
git merge -X theirs branch # Favor incoming branch in conflicts
# Viewing Merge Information
git log --merges # Show only merge commits
git log --oneline --graph # Visual merge history
git branch --merged # List branches merged into current
git branch --no-merged # List branches not yet merged
# After Conflicts
git status # See conflicted files
git add <file> # Mark conflict as resolved
git commit # Complete the mergeKey Takeaways
- Merging integrates changes from one branch into another
- Fast-forward merges simply move the branch pointer forward
- Three-way merges create a merge commit with two parents
- Always switch to the target branch before merging
- Use
git merge --abortto cancel a problematic merge --no-ffforces a merge commit even when fast-forward is possible- Merge commits have special messages and two parents
- Conflicts occur when the same lines are changed in both branches
- Test before merging and keep feature branches short-lived
- Delete feature branches after successfully merging them
What's Next?
Excellent progress! You now understand how to merge branches together, the different types of merges Git performs, and the workflows for integrating features into your main codebase. You've also gotten your first introduction to merge conflicts.
In the next lesson, we'll dive deep into handling merge conflicts. You'll learn exactly how to resolve conflicts, understand the conflict markers, use various tools to help with resolution, and master the techniques that make conflicts much less scary. Conflicts are a normal part of collaborative development, and knowing how to handle them confidently is an essential skill!
🎯 Practice Assignment
Before the next lesson, practice merging:
- Create 2-3 feature branches from main
- Make commits on each branch (different files)
- Practice merging them back to main one at a time
- Try a merge with
--no-ffto see the difference - Use
git log --graphto visualize your merges - Intentionally create a conflict (edit the same line in two branches) and try resolving it