How to Use Git Rebase Step by Step: A Practical Guide for Developers
If you’ve been using Git for a while, you probably feel comfortable with git merge. It’s safe, it’s predictable, and it gets the job done. But there’s another tool in your Git toolkit that can keep your project history cleaner and easier to read: git rebase.
For a long time, I avoided rebase entirely. I’d heard the warnings — “don’t rebase shared branches,” “you’ll rewrite history” — and honestly, the command-line interface intimidated me. But once I actually sat down and learned how to use git rebase step by step, it changed how I manage my branches.
In this tutorial, I’ll walk you through everything you need to know, from the basic concepts to interactive rebasing, conflict resolution, and real-world scenarios where rebase shines over merge.
What Is Git Rebase, Exactly?
Before we dive into commands, let’s make sure we understand what rebase actually does under the hood.
In simple terms, rebase reapplies your commits on top of a different base commit. Imagine you branched off main to work on a feature. While you were heads-down coding, someone else merged their work into main. Your branch is now behind.
You have two choices:
- Merge: Creates a new “merge commit” that ties your branch and
maintogether. Your branch’s history stays as-is, but you get an extra commit and a less linear history. - Rebase: Rewinds your branch commits, moves your branch pointer to the latest
main, then replays your commits one by one on top. The result looks like you just branched yesterday — a clean, linear history.
Here’s a visual way to think about it:
Before rebase:
A---B---C (main)
\
D---E (feature)
After rebase:
A---B---C---D'---E' (feature now based on C)
Your commits D and E are replayed as D’ and E’ on top of C. Same changes, new commit hashes.
Prerequisites
Before you follow along with this tutorial, make sure you have:
- Git installed (version 2.40 or later recommended — I’m using Git 2.47 as I write this in 2026). You can check with:
git --version
-
A basic understanding of Git fundamentals — commits, branches, staging area, and merging. If you know how to
git add,git commit, andgit merge, you’re ready. -
A practice repository. I strongly recommend creating a throwaway repo so you can experiment freely:
mkdir rebase-practice && cd rebase-practice
git init
- A configured default editor. Interactive rebase opens your terminal editor. If you haven’t set one:
# For VS Code
git config --global core.editor "code --wait"
# For Nano
git config --global core.editor "nano"
# For Vim
git config --global core.editor "vim"
One more thing: I’ll use a sample repository throughout this tutorial. You can create it yourself by following the setup steps, or apply the concepts directly to your own project.
Setting Up a Practice Repository
Let’s build a realistic scenario. We’ll create a main branch with a few commits, then a feature branch with its own commits.
# Initialize
mkdir rebase-practice && cd rebase-practice
git init
# Create initial file and commit
echo "Hello World" > index.html
git add index.html
git commit -m "Initial commit: add index.html"
# Add more commits to main
echo "<h1>My Website</h1>" >> index.html
git add index.html
git commit -m "Add heading to index.html"
echo "<p>Welcome to my site.</p>" >> index.html
git add index.html
git commit -m "Add paragraph to index.html"
Now create and switch to a feature branch:
git checkout -b feature/newsletter
Add some commits on the feature branch:
echo "<form>Newsletter signup</form>" > newsletter.html
git add newsletter.html
git commit -m "Add newsletter signup form"
echo "<input type='email'>" >> newsletter.html
git add newsletter.html
git commit -m "Add email input to newsletter form"
Now go back to main and add commits there (simulating teammate work):
git checkout main
echo "<footer>Copyright 2026</footer>" >> index.html
git add index.html
git commit -m "Add footer to index.html"
Your repository now looks like this:
A---B---C---F (main)
\
D---E (feature/newsletter)
Where:
– A = Initial commit
– B = Add heading
– C = Add paragraph
– D = Add newsletter signup form
– E = Add email input
– F = Add footer
The feature branch is now one commit behind main. This is the perfect scenario for a rebase.
How to Use Git Rebase Step by Step
Step 1: Switch to the Branch You Want to Rebase
Always make sure you’re on the branch that needs updating — not on main, not on some other branch:
git checkout feature/newsletter
This is one of the most common beginner mistakes: running rebase from the wrong branch. I’ve done it myself, and it creates a mess you then have to undo. Double-check with:
git branch
You should see * feature/newsletter.
Step 2: Run the Rebase Command
Now tell Git to rebase your current branch onto the latest main:
git rebase main
You’ll see output like this:
Rebasing (1/2)
Rebasing (2/2)
Successfully rebased and updated "feature/newsletter".
Git rewound your two feature commits (D and E), moved your branch pointer to commit F (the latest on main), then replayed your commits on top.
Your history now looks like:
A---B---C---F---D'---E' (feature/newsletter)
Step 3: Verify the Result
Check your commit log to see the new linear history:
git log --oneline --graph --all
You should see something like:
* e3a7f2c Add email input to newsletter form
* 8b1d4a0 Add newsletter signup form
* 2c9f1e0 Add footer to index.html
* a4b5c6d Add paragraph to index.html
* f1e2d3c Add heading to index.html
* 1a2b3c4 Initial commit: add index.html
No merge commit. No branching graph. Just a clean, straight line.
Verify your files are still intact:
cat index.html
cat newsletter.html
All your changes should be there — both the main branch changes (footer) and your feature branch changes (newsletter form).
Handling Rebase Conflicts
In the ideal case, rebase goes smoothly. But in the real world, conflicts happen — especially when your feature branch modifies the same lines that were changed on main.
Let’s create a conflict on purpose so you know exactly how to handle it.
Creating a Conflict Scenario
# Start fresh
cd ..
rm -rf rebase-practice
mkdir rebase-practice && cd rebase-practice
git init
# Create a file with one line
echo "Color: blue" > config.txt
git add config.txt
git commit -m "Set color to blue"
# Create feature branch
git checkout -b feature/dark-mode
# Change the same line on the feature branch
echo "Color: dark" > config.txt
git add config.txt
git commit -m "Change color to dark for dark mode"
Now go back to main and change that same line:
git checkout main
echo "Color: green" > config.txt
git add config.txt
git commit -m "Change color to green"
Running the Rebase and Seeing the Conflict
git checkout feature/dark-mode
git rebase main
This time, Git will stop and tell you there’s a conflict:
Auto-merging config.txt
CONFLICT (content): Merge conflict in config.txt
error: could not apply a1b2c3d... Change color to dark for dark mode
hint: Resolve all conflicts manually and mark them as resolved
hint: with "git add <file>..." and then run "git rebase --continue".
Resolving the Conflict Step by Step
1. Open the conflicting file:
cat config.txt
You’ll see Git’s conflict markers:
<<<<<<< HEAD
Color: green
=======
Color: dark
>>>>>>> a1b2c3d (Change color to dark for dark mode)
2. Edit the file to resolve the conflict. Decide what the final content should be. For example, let’s say you want to keep your feature branch change:
echo "Color: dark" > config.txt
Or you could combine changes, pick one side, or write something entirely new — it depends on what makes sense for your project.
3. Stage the resolved file:
git add config.txt
4. Continue the rebase:
git rebase --continue
Git may open your editor to let you edit the commit message. You can keep it as-is, modify it, or just save and close. The rebase will then continue.
If there are multiple conflicting commits, Git will stop at each one, and you repeat this process.
Aborting a Rebase
If things get too messy or you realize you made a wrong decision, you can cancel the entire rebase:
git rebase --abort
This returns your branch to exactly how it was before you started. No harm done. I’ve used this more times than I’d like to admit.
Interactive Rebase: The Real Power Move
Basic rebase is useful, but interactive rebase (git rebase -i) is where things get genuinely powerful. It lets you modify your commit history before replaying it.
With interactive rebase, you can:
- Reorder commits
- Squash multiple commits into one
- Reword commit messages
- Edit specific commits (to split them or add changes)
- Drop commits entirely
Starting an Interactive Rebase
git rebase -i main
Or, to rebase the last N commits on your current branch:
git rebase -i HEAD~3
This opens your text editor with something like:
pick e3a7f2c Add newsletter signup form
pick 8b1d4a0 Add email input to newsletter form
pick f5c6d7e Fix typo in form label
pick a9b0c1d Add validation to email input
# Rebase instructions:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
Squashing Commits Together
One of the most common interactive rebase workflows is squashing. Let’s say your feature branch has four commits, but two of them are just typo fixes. You want a clean history with meaningful commits.
Edit the file:
pick e3a7f2c Add newsletter signup form
squash 8b1d4a0 Add email input to newsletter form
pick f5c6d7e Fix typo in form label
squash a9b0c1d Add validation to email input
Save and close. Git will open another editor window for each squash, letting you edit the combined commit message:
# This is a combination of 2 commits.
# This is the 1st commit message:
Add newsletter signup form
# This is the 2nd commit message:
Add email input to newsletter form
# Please enter the commit message for your changes.
Edit as needed, save, and close. The result is a cleaner history with fewer, more meaningful commits.
Rewording a Commit Message
To change just the message without combining commits:
pick e3a7f2c Add newsletter signup form
reword f5c6d7e Fix typo in form label
pick a9b0c1d Add validation to email input
Git will stop at the reword commit and open your editor so you can change the message.
Dropping a Commit
To remove a commit entirely:
pick e3a7f2c Add newsletter signup form
drop f5c6d7e Fix typo in form label
pick a9b0c1d Add validation to email input
Or simply delete the line containing the commit you want to drop.
Reordering Commits
Just move the lines around in the editor:
pick a9b0c1d Add validation to email input
pick e3a7f2c Add newsletter signup form
This will replay the commits in the new order. Note that reordering can cause conflicts if the commits touch the same files, but Git will prompt you to resolve them just like a normal rebase conflict.
The Golden Rule of Rebase: When NOT to Rebase
I need to be very clear about this because it’s the single most important thing to remember about rebase:
Never rebase commits that have been pushed to a shared repository and that other people might have based work on.
When you rebase, you’re rewriting history. Your commits get new hashes. If someone else has pulled your original commits and built on top of them, and then you force-push your rebased version, their history and yours will diverge. You’ll create duplicate commits, merge headaches, and frustrated teammates.
Here’s the practical guideline:
- Your local feature branches that nobody else is using: Rebase freely. Have fun.
- Shared feature branches where teammates are collaborating: Use merge instead, or coordinate rebase carefully with your team.
- Main/develop/release branches: Almost always use merge. These branches should represent stable history.
If you accidentally rebase a shared branch and need to recover, you can use git reflog to find the original commits and git reset --hard to go back.
Force-Pushing After Rebase
When you rebase a branch that’s already been pushed to a remote, a normal git push will be rejected because your local history has diverged. You need to force-push:
git push --force-with-lease origin feature/newsletter
Always prefer --force-with-lease over --force. It checks that nobody else has pushed to the remote branch since your last pull, preventing you from accidentally overwriting a teammate’s work.
Rebase vs. Merge: Choosing the Right Tool
This is one of those perennial developer debates, and honestly, both tools have their place. Here’s my practical perspective:
Use Rebase When:
- You’re updating your local feature branch with the latest from
mainbefore submitting a pull request - You want to clean up messy “WIP” commits before sharing your work
- Your team follows a rebase-based workflow and has agreed on it
- You want a linear, easy-to-read project history
Use Merge When:
- You’re integrating a completed feature branch into
mainordevelop - You’re working on a shared branch with teammates
- You want to preserve the exact history of when branches diverged and merged
- You’re working with protected branches that shouldn’t be rewritten
What I Actually Do in Practice
On most projects I work on, here’s the workflow:
- Create feature branch from
main - Make commits freely (including messy “fix typo” and “WIP” commits)
- Regularly rebase onto
mainto stay up to date - Interactive rebase to clean up my commits before the PR
- Merge (usually via GitHub/GitLab’s merge button, often with squash merge) into
main
This gives you the best of both worlds: clean local development, and a clean main branch history.
Common Pitfalls and How to Avoid Them
Pitfall 1: Rebasing the Wrong Branch
I once accidentally ran git rebase feature/my-branch while on main. This replayed all of main‘s commits onto my feature branch tip, creating a spectacular mess.
How to avoid: Always run git branch before git rebase and confirm you’re on the correct branch. Make it a habit.
Pitfall 2: Forgetting to Stage After Conflict Resolution
When resolving conflicts during a rebase, you need to git add the resolved files before running git rebase --continue. If you forget, Git will tell you, but it can be confusing if you don’t know what’s happening.
How to avoid: After editing conflicted files, always:
git add .
git rebase --continue
Do NOT commit during a rebase. Just stage and continue.