CodecPath

Guide

Git Rebase Interactive: Rewrite Commit History Safely

Use interactive rebase to reorder, edit, squash, or remove private commits while preserving a recovery path and avoiding destructive force-pushes.

CodecPath editors6 min read
Private featurebranchNo teammate depends onits commitsBackup referencePreserves the originalcommit chainInteractive todoReorders, edits, orcombines commitsTests and diffChecks behavior andfinal changesForce with leaseRejects an unexpectedremote update
A safe rewrite keeps a named recovery point, verifies the replacement commits, and uses a lease before updating the remote branch.

When is rewriting commit history safe?

Rebase only private commits.

Once teammates may have based work on your feature branch, rewriting its existing commits changes their object IDs and forces everyone to reconcile two incompatible versions of the same history.

A local branch is the safest place for interactive rebasing. A personal branch already pushed to origin can also be reasonable if repository policy permits force-pushes and nobody else uses it.

Stop if the branch is shared. Merge the upstream branch instead, or create new commits that correct the old ones. The practical distinction between the two integration methods is covered in Git merge versus rebase.

Before starting, inspect the current branch and its upstream:

Bash
git status
git branch -vv
git log --oneline --decorate --graph -15

A clean working tree is required for the least surprising process. Commit intentional changes or stash them before rebasing, then create a recovery branch whose name will remain stable even as HEAD moves:

Bash
git branch backup/before-rebase

That branch is cheap insurance. Do not delete it until the rebased branch has passed its tests and, if applicable, survived review.

Which Git rebase command starts an interactive rewrite?

Run git rebase -i BASE.

Choose BASE as the commit immediately before the oldest commit you intend to change, because Git lists every current-branch commit after that boundary in the editor.

For the last five commits, use:

Bash
git rebase -i HEAD~5

To rewrite work added since the feature branch diverged from origin/main, first fetch the current remote state and then use the merge base:

Bash
git fetch origin
git rebase -i $(git merge-base HEAD origin/main)

Git's interactive rebase mode opens a todo file with commits in application order, oldest first. That is the reverse of the default git log display, and it is where hurried edits go wrong.

The official rebase documentation defines -i as the mode that allows commits to be reordered, edited, squashed, or removed. Read the todo from top to bottom, change only the command at the start of each line, then save the file and close the editor.

What do pick, reword, edit, squash, fixup, and drop do?

Each command rewrites differently.

Use reword for a commit message, edit for content or metadata, squash to combine changes while editing messages, fixup to discard the later message, and drop to remove a commit.

  • pick: Apply the commit unchanged.
  • reword: Apply its changes, then open the commit message editor.
  • edit: Pause after applying it so files, author details, or the commit itself can be changed.
  • squash: Fold it into the previous commit and open an editor for the combined message.
  • fixup: Fold it into the previous commit while normally keeping the previous message.
  • drop: Omit the commit and its changes from the rebased history.

Move whole lines to reorder commits. Interactive rebasing enables you to edit, remove, squash, or reorder commits, but every altered commit and every descendant receives a new object ID.

Be careful with drop. If later commits depend on code introduced by the removed commit, the process may stop with a conflict or produce a branch that builds incorrectly. Run the project's tests after the rewrite rather than trusting a clean rebase as proof of correct behavior.

How do you squash and reorder a feature branch?

Edit the todo list.

Keep the first logical commit as pick, mark its correction commits as fixup or squash, and move independent commits into the order a reviewer should receive them.

Suppose the editor opens this list:

Output
pick a12bc34 Add parser
pick b23cd45 Fix parser typo
pick c34de56 Add parser tests
pick d45ef67 Address review comment

Change it to:

Output
pick a12bc34 Add parser
fixup b23cd45 Fix parser typo
fixup d45ef67 Address review comment
pick c34de56 Add parser tests

Save the file and close the editor. Git applies the parser commit, folds both corrections into it, and then creates the test commit on top.

Order matters here. A fixup line combines with the previous surviving commit, not necessarily the commit it originally followed, so placing it beneath the wrong line silently targets the wrong change.

After the process completes, inspect the new commits rather than merely counting them:

Bash
git log --oneline --decorate --graph backup/before-rebase..HEAD
git diff backup/before-rebase...HEAD

The log should show a reviewable patch set. The diff should still contain the intended feature, although commit IDs and intermediate snapshots have changed.

How do you edit an older commit during rebasing?

Mark that commit edit.

When Git pauses at the selected commit, change the files, stage the corrected snapshot, amend it without creating an unrelated new commit, and then continue rebasing.

  1. Replace pick with edit on the required line.
  2. Save the todo file and close the editor.
  3. Modify the files while Git is paused.
  4. Stage the correction with git add <path>.
  5. Run git commit --amend to update the paused commit.
  6. Resume with git rebase --continue.

If only the previous commit message needs changing, reword is simpler. If the latest commit needs more changes and no interactive session is required, stage the files and run git commit --amend; amend is commonly used to add more changes to the latest commit.

Check git status whenever the state is unclear. It names the current operation, shows staged and unstaged paths, and tells you whether Git expects --continue, --skip, or --abort.

How should rebase conflicts be resolved?

Resolve one commit at a time.

For each stopped commit, inspect the conflicted paths, preserve the intended final behavior, stage every resolution, and run git rebase --continue so Git can apply the remaining commits.

  1. Run git status to identify unmerged paths.
  2. Open each file and remove the <<<<<<<, =======, and >>>>>>> conflict markers after choosing the correct content.
  3. Stage resolved files with git add <path>.
  4. Run git rebase --continue.
  5. Repeat if a later commit conflicts.

Use git rebase --skip only when the current commit's changes are already present or genuinely unnecessary. Skip discards that commit from the new history; it is not a generic way to dismiss a difficult conflict.

Get out cleanly if needed. git rebase --abort returns the branch to its state before the process began, provided the repository metadata remains intact. If an editor or hook fails, fix that specific failure and rerun git rebase --continue rather than starting a second rebase.

How do you recover from a bad interactive rebase?

Use the reflog first.

Git records recent movements of HEAD, so a commit apparently removed by a completed rebase can usually be recovered until reflog expiry and garbage collection make it unreachable.

Inspect the branch backup before doing anything destructive:

Bash
git log --oneline --decorate backup/before-rebase

If that reference contains the wanted history, restore it on a new branch:

Bash
git switch -c recovery/rebase backup/before-rebase

Without a backup branch, inspect the reflog:

Bash
git reflog --date=local

Find the entry before the rebase started, verify it with git show <commit>, and create a branch there. The git reflog reference documents these local reference updates and their expiration behavior.

Do not immediately run git reset --hard against a guessed hash. First attach a branch to the candidate commit, compare it with the current branch, and recover any uncommitted files separately because reflog records commits and reference movements, not arbitrary working-tree contents.

How do you push rewritten commits without overwriting teammates?

Use --force-with-lease only.

After fetching the remote, this option rejects the push when the remote branch no longer points to the commit your local repository expects, which protects intervening work that plain --force would replace.

Run the checks first:

Bash
git fetch origin
git log --oneline --left-right --graph origin/feature...feature
git push --force-with-lease origin feature

A rejected lease is useful information. Fetch again, inspect who changed the remote branch, and coordinate with that developer instead of escalating to --force.

The Git push documentation warns that force updates can lose commits and describes the protection provided by --force-with-lease. That protection still depends on an accurate remote-tracking reference, so automated fetch jobs and unusual push workflows deserve extra caution.

Never rewrite a protected main, master, release, or shared development branch merely to make its graph tidy. If other work is already based on those commits, add a corrective commit or merge instead.

FAQ

What is a safe Git rebase example?
A safe Git rebase example starts on a private feature branch with a clean working tree and a backup reference. Run git branch backup/before-rebase, then git rebase -i HEAD~3; mark correction commits as fixup, save, test the resulting branch, and push with git push --force-with-lease origin feature only if that remote branch is yours alone.
Does interactive rebase delete the original commits immediately?
No, the original commits usually remain reachable through a backup branch or local reflog for a limited period. They become candidates for garbage collection only after no reference reaches them and their reflog entries expire.
Can interactive rebase change a commit author?
Yes. Mark the commit as edit, let the rebase pause, then run git commit --amend --author="Name <email@example.com>" and git rebase --continue; descendants will receive new commit IDs as well.
Should I rebase a branch after someone else has checked it out?
No, not without explicit coordination. Their branch is based on the old commit IDs, so your rewritten version will require them to rebase, reset, or otherwise reconcile duplicated history.

Official software

Related guides