Scenario:
- You have a main branch (main).
- You create feature branches (e.g., feature-x) for development.
- You want to keep your history clean and structured.
1️⃣ git merge → Use When You Want to Keep Full History (Default Approach)
What It Does?
- Combines feature branch commits into
main, keeping all commit history. - Creates a new merge commit (
Fast-forwardif no diverging commits). - Easy to understand but can clutter history with many small commits.
When to Use?
- If you want a full commit history, including every small commit.
- When working in teams where visibility of each commit matters.
Example (Merging feature-x into main):
git checkout main
git pull origin main # Ensure main is up to date
git merge feature-x
git push origin main
2️⃣ git rebase → Use When You Want a Clean, Linear History
What It Does?
- Moves (
reapplies) all commits from the feature branch on top of the latestmain. - No merge commit → keeps history linear and clean.
- Can cause conflicts if multiple people are rebasing.
When to Use?
- When working solo and want a clean, linear history.
- Before merging a feature branch to avoid unnecessary merge commits.
- If you regularly update your feature branch with
main.
Example (Rebasing feature-x onto main Before Merging):
# 1. Switch to your feature branch
git switch feature-x
# Commit or stash your changes before rebasing
# Pull and rebase from remote main
# 2. git pull --rebase origin main
# If conflicts occur:
# Resolve conflicts in the affected files
git add <resolved-files>
git rebase --continue
# Repeat if additional conflicts occur.
#to cancel the rebase:
git rebase --abort
# Push your feature branch
git push origin feature-x
Before rebase:
main: A---B---C
\
feature-x: D---E
After rebase:
main: A---B---C
\
feature-x: D'---E'
After fast-forward merge:
main: A---B---C---D'---E'
3️⃣ git squash → Use When You Want a Single Commit for a Feature
What It Does?
- Combines multiple commits in the feature branch into one commit before merging.
- Keeps history very clean but loses commit granularity.
When to Use?
- If a feature branch has many small commits (e.g.,
fix typo,refactor,debugging). - When you want one clean commit per feature.
- If your team follows a “one commit per feature” policy.
Example (Squashing Commits Before Merging into main):
Step 1: Squash commits interactively
# Step 1
# Already on feature-x
git status
# Fetch latest remote changes
git fetch origin
# Squash feature commits
git rebase -i origin/main
# Step 2: Mark commits to squash
# Git opens an editor showing your feature commits:
pick a123456 Add media source model
pick b234567 Add media source API
pick c345678 Add media source validations
pick d456789 Add request specs
# Change pick to squash (or s) for the commits you want to combine:
pick a123456 Add media source model
squash b234567 Add media source API
squash c345678 Add media source validations
squash d456789 Add request specs
# Save and close the editor
# Set the final commit message
# Git opens another editor where you can write a consolidated commit message:
feat: add media source management
- Add media source model and validations
- Implement media source API endpoints
- Add request specs
# Save and close
# Verify the result
git log --oneline origin/main..HEAD
# Push your feature branch - If you haven't pushed this branch before:
git push -u origin feature-x
# If the branch was already pushed before squashing:
git push --force-with-lease origin feature-x
# Squashing rewrites commit history, so the remote branch may need this update.
Step 4: Merge into main
git checkout main
git merge feature-x
git push origin main
Another git squash example. Suppose you have created a branch for adding a feature and you do 2 more commits one for refactoring and other for commenting on the feature. Now you have 3 commits in the feature-x branch.
Now you think better I would commit these 3 commit together and push it as one feature commit. Now what you do? follow the bel0w steps:
# this tells git you want to do some action upon the last 3 commits
git rebase -i HEAD~3
Now you can see 3 commits in your default editor prefixed: pick. Change the last 2 commit pick to squash and save the file. Now Another file popup in the editor to change the commit message for this one squashed commit. Ignore the messages start with # . Keep/modify the commit message which is not starting with # and save the file. This rewrites the history and save as one commit.
Now check the commit history:
git log --oneline
Checkout git HEAD in our post: https://railsdrop.com/2025/04/06/git-best-practices-git-head-useful-commands-commit-message/
Important: Rebase vs Squash
| Operation | Command | Purpose |
|---|---|---|
| Rebase | git pull --rebase origin main | Update feature branch with latest main |
| Squash | git rebase -i origin/main | Combine multiple feature commits into fewer commits |
One important distinction: the command above squashes all commits on feature-x since it diverged from origin/main into one commit. If you want to preserve some commits and squash only selected ones, use the interactive editor accordingly.
Which One to Use in Your Development Process?
| Scenario | Use merge? | Use rebase? | Use squash? |
|---|---|---|---|
| Merging a feature branch | ✅ Yes (keeps history) | ✅ Yes (cleaner history) | ✅ Yes (one commit per feature) |
| Keeping history linear | ❌ No | ✅ Yes | ✅ Yes |
| Keeping every commit | ✅ Yes | ✅ Yes | ❌ No (loses small commits) |
| Team collaboration | ✅ Yes (safer) | ⚠️ Be careful (can rewrite history) | ✅ Yes (if team agrees) |
| Avoiding merge commits | ❌ No | ✅ Yes | ✅ Yes |
Recommendation for Your Workflow 🔀
For daily work, keep your feature branch updated with main:
git checkout feature-x
git pull origin main
git rebase main
Before merging into main, If you want full history:
git merge feature-x
If you want a clean history:
git rebase main
If you want a single commit per feature:
git squash
Merge into main and push:
git checkout main
git merge feature-x
git push origin main

🤔 What’s the Problem with Merge Commits?
A merge commit happens when you run:
git merge feature-x
and Git creates an extra commit to record the merge.
Example merge commit:
commit abc123 (HEAD -> main)
Merge: def456 ghi789
Author: You <you@example.com>
Date: Sat Mar 29 12:00:00 2025 +0000
Merge branch 'feature-x' into 'main'
Why Do Some Developers Avoid Merge Commits?
1️⃣ Cluttered History
- Merge commits pollute history when feature branches have multiple commits.
- Running
git logshows lots of merge commits, making it harder to track actual code changes. - Example of messy history with merge commits:
* Merge branch 'feature-x' (Merge Commit)* Fix typo in error message* Add validation for user input* Implement user authentication* Merge branch 'feature-y' (Merge Commit)* Fix UI issue
2️⃣ Harder to Track Changes
git loggets filled with merge commits instead of meaningful commits.git blamemight point to a merge commit, making debugging harder.
3️⃣ Confusing Graph with Many Branches
- Running
git log --graph --oneline --allshows a complex branching history:
* abc123 Merge branch ‘feature-x’
|\
| * ghi789 Fix authentication bug
| * def456 Add new login method
| 123456 Merge branch ‘feature-y’
|/ - If you rebase instead, the history stays linear.
🛠️ When Do Devs Avoid Merge Commits?
1️⃣ When They Want a Clean, Linear History
✅ Use git rebase instead of git merge to avoid merge commits.
Example:
git checkout feature-x
git rebase main
git checkout main
git merge feature-x # No merge commit (fast-forward merge)
🔹 This way, the commits from feature-x appear directly on top of main.
2️⃣ When They Squash Multiple Commits into One
✅ Use git rebase -i main to combine multiple commits into one before merging.
Example:
git checkout feature-x
git rebase -i main # Squash commits
git checkout main
git merge feature-x # No unnecessary commits
🔹 This results in one clean commit per feature.
3️⃣ When Working on a Team with Many Developers
- If multiple people merge their branches without rebasing, Git history gets full of merge commits.
- Teams using trunk-based development or feature branch workflows often rebase instead of merging.
✅ Solution → Always rebase before merging:
git checkout feature-x
git pull --rebase origin main
git push origin feature-x
When Are Merge Commits Actually Useful?
❌ Merge commits are not always bad! Sometimes, they are necessary:
- Merging Long-Lived Feature Branches
- If a branch exists for weeks or months, it has many commits.
- A merge commit documents when the feature was merged.
- Merging Changes from a Release Branch
- Example: Merging
release-v1.0intomainshould have a merge commit.
- Example: Merging
- Handling Complex Conflicts
- When resolving big merge conflicts, a merge commit shows exactly when conflicts were fixed.
🔑 Final Answer: When to Avoid Merge Commits?
🚫 Avoid merge commits when you want a clean history
✅ Use rebasing (git rebase) before merging to keep history linear.
✅ Use squashing (git rebase -i) to avoid unnecessary commits in a feature branch.
🔹 If you’re working solo or in a small team, rebase before merging to keep history simple.
🔹 If you’re working in a large team with long-lived branches, merge commits may be useful for tracking.
🔥 Finally Best Practices for Your Development Process
Before merging, update your branch without a merge commit:
git checkout feature-x
git pull --rebase origin main
Squash unnecessary commits (optional):
git rebase -i main
Merge the branch (fast-forward, no merge commit):
git checkout main
git merge feature-x # Fast-forward merge, no merge commit
This keeps your history clean and easy to read!