Skip to content

Rewriting history

Squash, fixup, reword, reorder and drop — as real rebases.

Every history command in stage is an interactive rebase. stage writes the todo list and gives it to git through GIT_SEQUENCE_EDITOR. There is no second implementation of rebase.

Each rebase runs with --autostash, so a dirty working tree does not stop you. git puts your changes back when the rebase finishes.

Press 4 for the commits panel. These keys act on the commit under the cursor.

Key Action
r Change the message
s Squash into the parent
f Squash into the parent, and keep the parent message
d Delete the commit
t Revert the commit with a new commit
g Reset the branch to the commit
e Stop the rebase here, so you can amend
ctrl+k / ctrl+j Move the commit up or down

For more than one change, plan them together. Press i on the oldest commit you want to touch.

  1. Press 4, then move the cursor down to an older commit.
  2. Press i. The plan opens with every commit set to pick.
  3. Press s, f, d, r or e to mark the commit under the cursor.
  4. Press J or K to move a commit.
  5. Press enter, then y.
Two marks, one execution. The plan says how many commits it rewrites.

The plan is only in memory, and the header says so. Nothing touches the repository until you press enter and answer the question.

You read a commit, you find a fault, and you write the correction. The fix belongs in the commit that caused it, not in a new commit at the top.

  1. Stage the correction.
  2. Move to the commit that owns the fault.
  3. Press F, then y. stage writes a fixup! commit for that commit.
  4. Press S, then y. Every fixup! commit folds into its target.
F records the fix against a commit. S folds every fixup in.

Press ctrl+f to let stage find the commit for you. stage reads the staged change, and looks for the commit that last touched those lines. It uses the staged change only, because F and A both consume the index.

stage refuses to rewrite a merge commit, and says which command refused:

cannot edit a merge commit
cannot squash into a merge commit
cannot move a merge commit
history above the target contains a merge commit — not supported yet

A flat todo list turns a merge into one ordinary commit, and the second parent is lost. stage stops instead.

A rebase replays commits, and a replay can conflict. Read Conflicts for the keys that continue, skip, or abort.

An edit step also stops the rebase, on purpose. stage says so, and m shows the options.

Press z to undo, and Z to redo. stage reads the reflog, so a rewritten commit is still reachable after the rewrite.

CAUTION: Do not rewrite commits that other people already pulled. A rewrite gives every commit after it a new hash.