Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Interactive Rebase: Clean Up Your History

By Kokil Thapa | Last reviewed: September 2026

Interactive Rebase: Clean Up Your History is the Git workflow you reach for when a feature branch reads like a diary instead of a changelog. You fixed a typo, rebased twice, and left three "WIP" commits before the real work landed. Reviewers hate that noise. CI logs get harder to bisect. A clean, linear story makes rollbacks and code review faster. If you already understand when to rebase versus merge, interactive rebase is the next skill that turns messy branches into merge-ready history.

What is interactive rebase and why should you clean up Git history?

Standard rebase replays commits onto a new base. Interactive rebase adds a pause step. Git opens an editor with a todo list. You choose what happens to each commit before Git applies the changes.

On production Laravel projects I maintain with ongoing support and maintenance, clean history is not vanity. It is operational hygiene. When a Deployer release breaks checkout on a sister site, I want one commit per logical change—not six fragments that say "fix fix fix".

Interactive rebase helps you:

  • Combine small fix commits into one readable unit
  • Split an oversized commit that mixed refactor and feature work
  • Reword vague messages like "updates" into something searchable
  • Drop accidental commits (debug dumps, wrong env files)
  • Reorder commits so tests pass at every step in the replay
Before vs After Interactive RebaseMessy branchWIPfixtypofeatfix2Hard to reviewHard to bisectClean branchfeattestdocsClear review storyEasy rollbackCI-friendly bisectgit rebase -i
Interactive Rebase: Clean Up Your History transforms noisy WIP commits into a linear, review-friendly sequence.

The trade-off is real. Rewriting history changes commit SHAs. Anyone who pulled the old branch must reset. That is why interactive rebase belongs on feature branches you own—not on shared main without team agreement.

How do you start an interactive rebase to clean up your Git history?

Pick your base carefully. Most feature work rebases against the integration branch tip or a fixed number of commits back.

Rebase the last N commits on your current branch

If your branch has five commits you want to tidy, run:

git checkout feature/booking-refactor
git fetch origin
git rebase -i HEAD~5

Git opens your configured editor with a file like .git/rebase-merge/git-rebase-todo. Each line is one commit, oldest at the top.

Rebase everything since you branched from main

This is the pattern I use before opening a merge request on GitLab CI pipelines:

git fetch origin
git rebase -i origin/main

That includes every commit on your branch that is not on origin/main. It is ideal when you branched days ago and accumulated noise. For a deeper comparison of integration styles, see git rebase vs merge when to use each.

Step-by-step workflow

  1. Ensure your working tree is clean: git status must show nothing unstaged.
  2. Fetch remote refs so your base is current.
  3. Run git rebase -i with the correct range.
  4. Edit the todo list (next section covers commands).
  5. Save and close the editor; Git replays commits one by one.
  6. Resolve conflicts if Git stops; use git rebase --continue.
  7. Run tests locally before any push.
  8. Push with lease: git push --force-with-lease origin feature/booking-refactor.
Interactive Rebase Workflowfetchupdate refsrebase -iedit todoresolveconflictsrun testslocal CIpush leaseforce-with-leaseExample todo snippetpick a1b2c3 Add booking validationsquash d4e5f6 Fix typo in labelfixup g7h8i9 Remove debug dump
A typical interactive rebase pipeline: fetch, edit the todo list, fix conflicts, test, then force-push with lease.

On a Laravel booking app like Adventure Third Pole Trek, I often squash "fix migration" and "fix seeder" into the feature commit that introduced the schema change. Reviewers see one diff block. QA sees one revert target if something fails in staging.

What do pick, squash, fixup, and other interactive rebase commands do?

The todo file is the control panel. Git documents every action in the official git-rebase manual. These are the commands you will use daily.

CommandWhat it doesWhen to use it
pickKeeps the commit as-isCommits that already tell a clear story
rewordKeeps changes but opens editor for messageFixing "update" or ticket-only messages
squashMerges into previous commit; combines messagesGrouping related small commits with context
fixupMerges into previous commit; discards messageTypos, lint fixes, debug removal
dropRemoves the commit entirelyAccidental or empty commits
editPauses rebase so you can amend the commitSplitting one commit into two logical units

Example: squash three fix commits into one feature commit

Your todo might start like this:

pick 8a1f2b3 Add Livewire booking step component
pick 3c4d5e6 Fix validation rule typo
pick 7g8h9i0 Fix CSS spacing on mobile
pick 1j2k3l4 Fix forgotten translation string

Change it to:

pick 8a1f2b3 Add Livewire booking step component
fixup 3c4d5e6 Fix validation rule typo
fixup 7g8h9i0 Fix CSS spacing on mobile
fixup 1j2k3l4 Fix forgotten translation string

Result: one commit with all four diffs. The final message stays as the first line unless you used squash and edited the combined message in the follow-up editor.

Example: reword a vague commit message

reword 9x8y7z6 Update stuff
pick 5a4b3c2 Add policy for booking cancellation

Git stops after the first replay and opens your editor. Replace "Update stuff" with something like Restrict booking edits to confirmed reservations. Good messages match what you would want in a Laravel best practices changelog.

Example: split an oversized commit with edit

Mark the bloated commit with edit. When Git pauses:

git reset HEAD~1
git add app/Http/Requests/BookingRequest.php
git commit -m "Add Form Request validation for bookings"
git add resources/views/livewire/booking-step.blade.php
git commit -m "Add Livewire view for booking step"
git rebase --continue

This pattern mirrors clean architecture separation—validation layer first, UI second.

Rebase Command Effectspickkeep commitsquashmerge + msgfixupmerge silentdropremoveFour commits become oneABCDA+fixup B,C,D into A — one SHA, one message
Pick keeps commits; squash and fixup fold follow-ups into the parent; drop removes noise entirely.

A practical tip: prefer fixup over squash when the follow-up message adds no value. You avoid an extra editor step. If you need to craft a single polished message from three fragments, use squash and edit the result.

How do you safely interactive rebase without breaking shared branches?

Rewriting published history is the main risk. Force-pushing over someone else's work causes duplicate commits and painful merges. Follow these rules on every team I deploy for via Linux system administration and GitLab CI.

Golden rules

  • Never interactive rebase main, master, or production without explicit team policy.
  • Only rebase branches you alone are working on—or coordinate in chat first.
  • Always use --force-with-lease, never bare --force.
  • Rebase before review when possible; after approval, prefer a merge unless policy requires squash.
  • Announce in the MR if you rewrote history after someone else fetched your branch.

--force-with-lease refuses to push if the remote moved since your last fetch. That blocks overwriting a teammate's push by accident.

git push --force-with-lease origin feature/notary-document-upload

On legal-tech portals such as Notary Kathmandu, document-upload features often touch sensitive paths. A clean branch makes audit trails in merge requests easier for non-developer stakeholders.

When teammates already pulled your branch

They must realign after your force-push:

git fetch origin
git checkout feature/notary-document-upload
git reset --hard origin/feature/notary-document-upload

Warn them first. Uncommitted local work on that branch will be lost with a hard reset.

Integrate with CI pipelines

After rebasing, your pipeline runs on new SHAs. That is expected. On sister sites sharing Deployer 7 + GitLab CI, I run PHPUnit and static checks locally before push. It saves a failed pipeline slot. Pair this habit with AI code review in CI only after history is stable—review tools diff against the target branch, and churn mid-review wastes everyone time.

Safe to Rebase?Is branch shared?YesDo not rebasemerge or new commitNorebase -i OKforce-with-leaseProtected branches: main, master, productionFeature branches: squash WIP before MRPost-merge: never rewrite published integration history
Interactive rebase is safe on private feature branches; avoid rewriting shared or protected integration branches.

How do you recover when an interactive rebase goes wrong?

Git keeps a reflog. Almost every failed rebase is reversible if you act before garbage collection prunes old entries.

Abort mid-rebase

git rebase --abort

That returns your branch to the state before you started. Use this when conflicts spiral or you picked the wrong base.

Restore the branch to a known good commit

git reflog
git reset --hard HEAD@{3}

Find the entry from before rebase -i started. The Pro Git book chapter on rewriting history explains reflog semantics in depth.

Rescue work after a bad force-push

If you force-pushed the wrong history, a teammate—or you on another machine—may still have the old commits locally. Find the old tip SHA in their reflog or yours, then:

git branch recovered/feature 8a1f2b3
git push origin recovered/feature

On client portal projects, I document the pre-rebase SHA in the merge request description when the diff is large. Recovery takes minutes instead of hours.

Common failure modes

  • Empty commit after squash: Git may skip empty commits; use git commit --allow-empty only when you truly need a marker.
  • Conflict during replay: Fix files, git add, then git rebase --continue—same as a normal rebase.
  • Wrong commit order: Abort and reorder the todo list; tests should pass at each replay step.
  • Accidentally dropped a commit: Recover SHA from git reflog and git cherry-pick it back.

When debugging messy diffs, a JSON formatter or regex tester helps validate API fixture changes—but fix the Git story first so you know which commit introduced the bug. That pairs well with AI-assisted debugging workflows where bisect needs clean boundaries.

Key Takeaways

  • Run git rebase -i HEAD~N or git rebase -i origin/main on feature branches before opening or updating a merge request.
  • Use fixup for noise commits and squash when you need one combined message.
  • Push with git push --force-with-lease—never rewrite main or shared branches without coordination.
  • Keep one logical change per commit so CI bisect and rollbacks stay predictable.
  • Use git rebase --abort and git reflog as your safety net when something goes sideways.
  • Align clean history with team policy: some orgs squash-on-merge and skip local interactive rebase entirely.

People Also Ask

What is the difference between squash and fixup in interactive rebase?

squash merges a commit into the previous one and opens an editor to combine commit messages. fixup merges the changes but discards the follow-up message entirely. Use fixup for typo and lint fixes; use squash when the combined message should explain the full change.

Can you interactive rebase after pushing to remote?

Yes, on a feature branch you own. Rewrite locally, then force-push with lease. Do not rebase if teammates have based work on your old commits unless you coordinate their reset. Never rebase shared integration branches without team rules that allow it.

How many commits should you squash before a merge request?

Aim for one commit per logical unit: feature, test addition, or docs update. Three to seven commits on a large feature is normal. Fifty micro-commits is a signal to squash. Some teams prefer one squash commit per MR via GitLab "Squash on merge"—interactive rebase still helps you validate the story locally first.

Does interactive rebase change file content or only history?

It rewrites history, not intent. The final tree should match what you would get from merging the messy branch—unless you dropped or edited commits. Always run your test suite after rebasing. Content bugs mean a bad drop or conflict resolution, not a rebase quirk.

Ship cleaner branches on your next Laravel or WordPress project

Interactive Rebase: Clean Up Your History is a daily skill for anyone shipping via GitLab CI, Deployer, or plain SSH deploys. It turns noisy feature work into review-ready commits without changing what you built. Start on a throwaway branch, practice squash and fixup, then adopt force-with-lease on real merge requests.

If your team needs help tightening Git workflow, CI pipelines, or custom software delivery on PHP 8.3+ and Laravel 12/13, see the portfolio for production examples or testing and optimization services. For workflow questions on an active retainer, contact us—or read more on about me and server provisioning with Ansible for the full delivery stack.

Frequently Asked Questions

Interactive rebase runs git rebase -i, which pauses before replaying commits. Git opens a todo list where you pick, squash, fixup, reword, edit, reorder, or drop each commit, then rewrites branch history accordingly.

Noisy branches with WIP and repeated fix commits slow code review and make CI bisect harder when a Deployer release breaks staging. Clean, linear history gives reviewers one diff per logical change, clearer rollback targets, and searchable messages instead of vague updates. On production Laravel projects I maintain, that is operational hygiene—not vanity. Interactive rebase turns diary-style commits into a merge-ready story without changing what you built, as long as you run tests after replay.

Ensure git status shows a clean working tree, then fetch so your base is current. To tidy the last five commits on your branch, checkout the feature branch and run git rebase -i HEAD~5. To rebase everything since you branched from main—the pattern I use before opening a GitLab merge request—run git fetch origin then git rebase -i origin/main. Git opens your configured editor with .git/rebase-merge/git-rebase-todo, oldest commit at the top. Edit the list, save, resolve any conflicts with git rebase --continue, run tests locally, then push.

squash merges a commit into the previous one and opens an editor to combine commit messages. fixup merges the changes but discards the follow-up message entirely. Use fixup for typo, lint, and debug-removal noise; use squash when the combined message should explain the full change.

pick keeps a commit unchanged when its message and diff already tell a clear story. reword keeps the changes but opens the editor so you can replace vague messages like updates with something searchable. drop removes a commit entirely—useful for accidental debug dumps or wrong env files. edit pauses the rebase at that commit so you can amend or split it; mark a bloated commit with edit, run git reset HEAD~1, create separate commits for validation and UI layers, then git rebase --continue. Together with squash and fixup, these commands are your daily control panel.

Yes, on a feature branch you own. Rewrite history locally, then git push --force-with-lease. Never rebase if teammates based work on your old commits unless you coordinate their reset first.

Never interactive rebase main, master, or production without explicit team policy. Only rebase branches you alone are working on, or coordinate in chat first. Always use git push --force-with-lease instead of bare --force—it refuses to push if the remote moved since your last fetch. Rebase before review when possible; after approval, prefer a merge unless policy requires squash. Announce in the merge request if you rewrote history after someone fetched your branch. On legal-tech portals with sensitive paths, clean branches also make audit trails in merge requests easier for non-developer stakeholders.

Rewriting history changes commit SHAs, so a normal push is rejected and you must force-push. Bare --force overwrites the remote branch unconditionally. --force-with-lease adds a safety check: it refuses to push if origin moved since your last fetch, blocking accidental overwrite of a teammate's push. After interactive rebase on a feature branch you own, run git push --force-with-lease origin your-branch-name. Pair that with running PHPUnit and static checks locally before push on GitLab CI pipelines—it saves a failed pipeline slot when new SHAs trigger CI.

Mid-rebase, run git rebase --abort to return to the pre-rebase state when conflicts spiral or you picked the wrong base. After a completed but wrong rebase, run git reflog, find the entry from before rebase -i started, and git reset --hard HEAD@{N} to that point. If you force-pushed bad history, recover the old tip SHA from reflog—yours or a teammate's—and create git branch recovered/feature OLD_SHA then push it. For a dropped commit, cherry-pick the SHA back from reflog. Document the pre-rebase SHA in large merge requests so recovery takes minutes instead of hours.

Use HEAD~N when you know exactly how many recent commits need tidying—HEAD~5 rewrites only the last five on your current branch. Use git rebase -i origin/main after git fetch origin when you want every commit on your branch that is not on origin/main included—ideal if you branched days ago and accumulated WIP noise. The origin/main pattern matches what I run before opening a merge request on GitLab CI. Pick the range that covers all commits you intend to squash or reword; rebasing too few leaves mess, too many risks touching unrelated work.

Aim for one commit per logical unit—feature, test addition, or docs update. Three to seven commits on a large feature is normal; fifty micro-commits is a signal to squash.

Interactive rebase rewrites history, not intent. The final tree should match what you would get from merging the messy branch—unless you dropped commits or resolved conflicts incorrectly. It does not silently change application logic on its own. Always run your full test suite after rebasing. If tests fail afterward, suspect a bad drop, wrong conflict resolution, or incorrect commit order in the todo list—not a rebase quirk. Reorder commits so tests pass at every replay step when possible.

Mark the bloated commit with edit in the todo list. When Git pauses at that commit, run git reset HEAD~1 to unstage its changes while keeping files in your working tree. Stage and commit logical slices separately—for example, git add the Form Request file and commit with a validation message, then git add the Livewire view and commit with a UI message. This mirrors clean separation between validation layer and presentation. When both commits exist, run git rebase --continue. Use this when one commit mixed refactor, feature work, and unrelated fixes reviewers cannot bisect cleanly.

Warn them before you force-push; uncommitted local work on that branch will be lost. They should run git fetch origin, checkout the feature branch, then git reset --hard origin/feature-branch-name to realign with the rewritten remote. Without that reset, they retain old SHAs and risk duplicate commits or painful merges when integrating. Announce the rewrite in the merge request if someone already fetched your branch before cleanup. Interactive rebase is safe on private feature branches you own; coordination is mandatory once others have pulled the pre-rebase history.

No—not without explicit team policy and coordination. Rewriting published integration history changes SHAs for everyone; force-pushing over shared branches causes duplicate commits and painful merges. Interactive rebase belongs on feature branches you alone work on, before review when possible. Some organisations squash on merge via GitLab and skip local interactive rebase entirely—align with team policy. Never rebase protected branches casually. On sister sites using Deployer 7 and GitLab CI, keep main linear through merge or squash-on-merge rules, and save git rebase -i for your feature/booking-refactor-style branches before the merge request opens.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: