Git Interactive Rebase
Introduction
When working on projects with Git, your commit history can quickly become cluttered with numerous small commits, work-in-progress updates, or confusing commit messages. This is where Git's interactive rebase comes in—a powerful feature that allows you to modify your commit history by reordering, combining, editing, or even removing commits before sharing your work.
Interactive rebase gives you the ability to present a clean, logical commit history that tells a coherent story about how your project developed. Think of it as editing a rough draft before publishing the final version.
What is Interactive Rebase?
Interactive rebase is an extension of Git's regular rebase command that gives you fine-grained control over your commit history. While normal rebase moves commits from one branch to another, interactive rebase lets you stop and modify each commit during the process.
With interactive rebase, you can:
- Reorder commits
- Edit commit messages
- Combine (squash) multiple commits
- Split commits into smaller ones
- Remove commits entirely
- Pause at each commit to update its content
When to Use Interactive Rebase
Interactive rebase is most useful in these scenarios:
- Before pushing commits to a shared repository
- When preparing a pull request for review
- When cleaning up your local development history
- Before merging a feature branch into the main branch
Important note: You should only rebase commits that exist in your local repository and haven't been pushed to a shared repository. Rebasing public history can cause serious problems for your collaborators.
The Basic Command
The basic syntax for interactive rebase is:
git rebase -i <base>
Where <base>
is the commit or branch that you want to rebase onto. This could be:
- A specific commit hash
- A branch name
- A relative reference like
HEAD~3
(three commits before the current HEAD)
Step-by-Step Interactive Rebase
Let's walk through a practical example of using interactive rebase to clean up a feature branch with several commits.
Example Scenario
Imagine you've been working on a feature branch with the following commits:
$ git log --oneline
abc1234 Fix typo in login button
def5678 Add password validation
ghi9101 WIP: Implement login form
jkl1121 Start work on user authentication
The history is a bit messy—it has a work-in-progress commit and a typo fix that could be combined with the related work. Let's clean it up!
Step 1: Start the Interactive Rebase
To start an interactive rebase that includes the last 4 commits:
git rebase -i HEAD~4
Step 2: The Rebase Todo List
Running this command opens a text editor with the rebase "todo" list:
pick jkl1121 Start work on user authentication
pick ghi9101 WIP: Implement login form
pick def5678 Add password validation
pick abc1234 Fix typo in login button
# Rebase commands:
# 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 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 = create a merge commit using original merge commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
Step 3: Edit the Todo List
Now you can modify this file to achieve your desired history. Let's make these changes:
- Keep the first commit as is
- Change the WIP commit to "edit" so we can modify it
- Keep the password validation commit
- Use "fixup" for the typo fix to combine it with the previous commit
Updated todo list:
pick jkl1121 Start work on user authentication
edit ghi9101 WIP: Implement login form
pick def5678 Add password validation
fixup abc1234 Fix typo in login button
Save and close the editor to start the rebase process.
Step 4: Editing the WIP Commit
Git will pause at the "edit" commit. Now you can:
- Change the files as needed
- Stage the changes with
git add
- Update the commit with
git commit --amend
- Rename the commit message from "WIP: Implement login form" to "Implement login form"
- Continue the rebase with
git rebase --continue
# Make your changes to the files
$ git add login-form.js
$ git commit --amend -m "Implement login form"
$ git rebase --continue
Step 5: Complete the Rebase
Git will apply the remaining operations automatically. The "fixup" will combine the typo fix with the password validation commit.
Final Result
After the rebase completes, your history will look like:
$ git log --oneline
def5678 Add password validation
ghi9101 Implement login form
jkl1121 Start work on user authentication
Notice how the history is now cleaner, more logical, and tells a better story of how the feature was developed.
Common Interactive Rebase Commands
Here's a more detailed explanation of the most common rebase commands:
pick
pick <commit>
Use the commit as is, without modifications.
reword
reword <commit>
Use the commit but edit the commit message. Git will open your text editor to modify the message.
edit
edit <commit>
Pause the rebase at this commit, allowing you to make changes to the files, amend the commit, or even add new commits before continuing.
squash
squash <commit>
Combine this commit with the previous commit. Git will prompt you to edit the combined commit message.
fixup
fixup <commit>
Like squash, but discard this commit's message and use only the previous commit's message.
drop
drop <commit>
Remove this commit from history entirely.
Visualizing the Rebase Process
Here's a Mermaid diagram illustrating the interactive rebase process:
Resolving Conflicts During Rebase
Conflicts can occur during a rebase if the changes you're rebasing conflict with changes in the base branch. When a conflict occurs:
- Git will pause the rebase
- Show you which files have conflicts
- Let you resolve the conflicts manually
- After resolving, you can continue with
git add
followed bygit rebase --continue
# If conflicts occur
$ # Edit the files to resolve conflicts
$ git add <resolved-files>
$ git rebase --continue
If you decide you want to abandon the rebase, you can use:
$ git rebase --abort
Real-World Examples
Example 1: Preparing a Pull Request
Before submitting a pull request, you can clean up your feature branch:
# See the current history
$ git log --oneline
abc1234 Fix test cases
def5678 Add documentation
ghi9101 Implement feature X
jkl1121 Initial commit for feature X
# Start interactive rebase
$ git rebase -i jkl1121^
In the editor, you might decide to:
pick jkl1121 Initial commit for feature X
squash ghi9101 Implement feature X
pick def5678 Add documentation
fixup abc1234 Fix test cases
This would result in a cleaner two-commit history:
# After rebase
$ git log --oneline
def5678 Add documentation
jkl1121 Initial commit and implementation of feature X
Example 2: Breaking Down a Large Commit
If you have a large commit that should be split into smaller, logical commits:
# Start interactive rebase
$ git rebase -i HEAD~3
In the editor, mark the large commit for editing:
pick abc1234 Small fix
edit def5678 Large feature implementation
pick ghi9101 Another small fix
When Git pauses at the large commit:
# Reset the commit but keep the changes staged
$ git reset HEAD^
# Create multiple smaller commits
$ git add file1.js
$ git commit -m "Implement feature part 1: data model"
$ git add file2.js file3.js
$ git commit -m "Implement feature part 2: UI components"
# Continue the rebase
$ git rebase --continue
Best Practices
-
Only rebase local commits: Never rebase commits that have been pushed to a shared repository.
-
Create a backup branch: Before an extensive rebase, create a backup branch.
bashgit branch backup-before-rebase
-
Keep rebases small: Try to rebase small groups of commits rather than long chains.
-
Verify after rebasing: Always check your history after rebasing to ensure it looks as expected.
bashgit log --oneline --graph
-
Push with force-with-lease: If you must push rebased history to a remote branch that you've previously pushed to, use:
bashgit push --force-with-lease
This is safer than
--force
as it prevents overwriting others' work.
Potential Issues and Solutions
1. Lost Commits
If you accidentally lose commits during a rebase, you can recover them using:
# Find the lost commit hash
$ git reflog
# Create a new branch pointing to that commit
$ git branch recover-branch <commit-hash>
2. Rebase on Wrong Branch
If you started a rebase on the wrong branch, you can abort it:
$ git rebase --abort
3. Complex Conflicts
For complex conflicts, you might want to use a graphical merge tool:
$ git mergetool
Summary
Git interactive rebase is a powerful tool for cleaning up your commit history before sharing it. By understanding how to reorder, combine, edit, or remove commits, you can maintain a clean and logical project history that clearly communicates the evolution of your code.
Remember to use interactive rebase only on local commits that haven't been shared with others. With practice, interactive rebase will become an essential part of your Git workflow, helping you present your work in a clear, professional manner.
Further Learning
To deepen your understanding of Git interactive rebase, consider exploring:
- Git's official documentation on rebasing
- Related commands like
git cherry-pick
andgit commit --amend
- Advanced branching strategies that work well with rebasing
- Team workflows that incorporate rebasing
Practice Exercises
-
Create a feature branch with 5-6 small commits, then use interactive rebase to condense them into 2-3 logical commits.
-
Practice splitting a large commit into smaller ones using the
edit
command. -
Use interactive rebase to reorder commits so related changes are grouped together.
-
Try resolving a merge conflict during an interactive rebase.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)