skip to content

questions

6

In Git, what happens when you run git rebase -i HEAD~3?

level: juniorimportance: must knowfreq 78%

answer

  1. it is a script, not a dialog
  2. an editor opens with a plan
  3. the oldest commit sits at the top
  4. each line starts with a verb like pick
  5. verbs plus line order drive the replay

basics

~20 s

Git opens an editor holding a todo list of the three commits after HEAD~3, oldest first, each on a pick line. You edit the verbs and the order, save, and Git replays the commits as scripted, creating new commit IDs.

solid answer

~40 s

`git rebase -i HEAD~3` means "replay everything after `HEAD~3` onto `HEAD~3`, but let me edit the plan first". Git writes a **todo list** — one line per commit, `pick <sha> <subject>`, **oldest at the top**, the opposite of `git log` order — and opens it in your editor. You change the leading verb (`pick`, `reword`, `edit`, `squash`, `fixup`, `drop`, plus `exec` and `break` lines), reorder lines to reorder commits, or delete a line to drop that commit. On save Git checks out the base, applies each entry in order, and moves the branch to the new tip. Every replayed commit is a **new object with a new SHA**, even the untouched ones, because its parent and committer timestamp changed. The base commit itself is never touched — the range is exclusive.

code

text · 11 lines
text
pick 9f2a1c3 add parser skeleton
pick 4b7e0aa fix typo in parser
pick c11d902 add parser tests

# Commands:
# p, pick   = use commit
# r, reword = use commit, but edit the message
# e, edit   = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup  = like squash, but discard this commit's message
# d, drop   = remove commit

go deeper

for a junior

Be ready to open git rebase -i HEAD~3, name the common verbs, and say that the oldest commit is at the top and the range excludes the base commit.

for a middle

Explain what happens on save: detached checkout at the base, entries applied in order, brand-new commit objects with new SHAs, branch ref moved only at the end.

for a senior

Show judgment about when to rewrite at all — unpublished branch work, cleanup before review — and how you recover a rebase that went wrong instead of panicking.

for a principal

Frame history rewriting as a team-wide contract: which branches are rewritable, what reviewers should get, and how much cleanup is worth the risk of rewriting shared work.

## What the command actually means `git rebase -i <base>` says: take every commit reachable from `HEAD` but not from `<base>`, let me script what happens to them, then re-apply them one at a time on top of `<base>`. The `-i` (`--interactive`) part only adds the scripting step; the replay itself is ordinary rebase. The range is **exclusive** at the base end. `HEAD~3` names the commit three back from the tip, so the todo list contains the **three commits after it** — the base commit is untouched. `git rebase -i <sha>` starts just after that sha. To include the very first commit of the repository you need `git rebase -i --root`, because the root commit has no parent to use as a base. ## The todo list Git writes a plain text file (`.git/rebase-merge/git-rebase-todo`) and opens it in the editor named by `GIT_SEQUENCE_EDITOR`, then `sequence.editor`, then your normal `core.editor`. It looks like: pick 9f2a1c3 add parser skeleton pick 4b7e0aa fix typo in parser pick c11d902 add parser tests Two things surprise people. First, the order is **oldest at the top** — the file reads like a script that runs downward, not like `git log`, which shows newest first. Second, the lines are **instructions, not a report**: whatever the file says when you save is what Git does. The commonly used verbs, each with a one-letter alias: - `pick` (`p`) — apply the commit unchanged. - `reword` (`r`) — apply it, then open an editor for its message only. - `edit` (`e`) — apply it, then **stop** with the commit already made so you can amend it, add files, or split it. - `squash` (`s`) — combine into the previous line's commit and open an editor with both messages. - `fixup` (`f`) — combine into the previous line's commit and discard this one's message. - `drop` (`d`) — discard the commit. Deleting the line entirely does the same thing. - `exec` (`x`) — not a commit at all; run a shell command at this point in the replay. - `break` (`b`) — stop here with no commit to edit, so you can look around. With `--rebase-merges` the todo can also contain `label`, `reset` and `merge` lines that rebuild a branch topology, and recent Git adds `update-ref` lines that carry other branch tips along. ## What Git does when you save Git checks out the base commit in a detached HEAD state and works through the todo list top to bottom, applying each commit's *change* rather than moving the commit object. Because a commit's SHA is a hash of its content **including its parent and its committer timestamp**, every replayed commit is a brand-new object with a new SHA — even the ones you left as `pick`. Only when the whole list finishes does Git move the branch ref to the new tip. If a step conflicts, Git stops and you resolve and run `git rebase --continue` (or `--skip` / `--abort`) — the general replay-and-conflict machinery is the same for interactive and non-interactive rebase. If you realise mid-rebase that the plan itself was wrong, `git rebase --edit-todo` reopens the remaining list. ## Practical notes - Deleting **all** lines and saving aborts the rebase instead of deleting all the commits — a deliberate safety valve. Lines starting with `#` are comments. - `squash` and `fixup` combine into the line **above**, so the first line can never be one of them; Git rejects the todo if it is. - The pre-rebase commits are not destroyed the moment the rebase runs — they remain reachable through the reflog until it expires, which is what makes a bad rebase recoverable. - Because every commit is rewritten, a branch you already pushed will diverge from its remote and need a force push. Rewriting history that other people have already pulled is the well-known hazard here. - Interactive rebase is for **your own unpublished work**: cleaning up a five-commit branch into a reviewable story before you open it for review.

  • Why do commits you left as plain pick still end up with new SHAs?
    A commit's SHA hashes its tree, message, author and committer identity and timestamps, and its parent. Replaying gives it a new parent (or at least a new committer timestamp), so the hash changes. Rebase copies changes, it never moves commit objects, which is why every commit from the first rewritten one onward is new.
  • What happens if you delete every line in the todo list and save?
    Git treats an empty todo as a request to abort: it reports that nothing was done and leaves the branch exactly where it was. It is a deliberate escape hatch, so an empty file is never read as "drop all these commits". To actually discard commits you mark them `drop` or delete only their lines.
  • How do you reorder two commits, and when does that go wrong?
    Swap their lines in the todo list. It works cleanly when the commits touch different code, and conflicts when the later one depends on the earlier one — Git will stop on the dependent commit with a conflict, since the hunk it expects is not there yet. Reorder only commits that are genuinely independent.

The todo list is a shooting script for your branch: Git re-films the same scenes in whatever order and with whatever edits the script says, and every take that comes out is a new reel, not the original.

saying these in an interview costs you the question

  • Thinking the todo list reports what Git already did
  • Assuming the top line is the newest commit
  • Believing untouched pick commits keep their original SHAs
  • Expecting the base commit named on the command line to be included
  • Treating interactive rebase as safe on a branch others already pulled

context

open as a page

In a Git interactive rebase todo list, how do squash and fixup differ?

level: middleimportance: must knowfreq 70%

basics

~20 s

Both fold a commit into the one on the line above it. Squash opens an editor with both commit messages so you can write a combined one; fixup keeps the earlier commit's message and throws the folded commit's message away, with no editor prompt.

open as a page

What does Git's rebase --autosquash option do to the todo list?

level: middleimportance: should knowfreq 42%

basics

~20 s

With --autosquash, git rebase -i scans commit subjects for the fixup! and squash! prefixes, moves each such commit directly below the commit it names, and pre-sets its todo verb to fixup or squash — so the plan arrives already arranged.

open as a page

How do you split one commit into two using Git interactive rebase?

level: middleimportance: should knowfreq 45%

basics

~20 s

Mark the commit edit in the git rebase -i todo list. When the rebase stops there, run git reset HEAD^ to undo the commit but keep its changes in the working tree, commit the pieces separately with git add -p, then run git rebase --continue.

open as a page

In a Git interactive rebase, how do you run the test suite on every commit?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Use git rebase -i --exec "make test" <base>. Git inserts an exec line after every commit in the todo list and runs that command as each commit is created; a non-zero exit stops the rebase at that commit so you can fix it and continue.

open as a page

How do you drive a Git interactive rebase from a script without an editor?

level: seniorimportance: nice to knowfreq 20%

basics

~10 s

Set GIT_SEQUENCE_EDITOR to a command that edits or simply accepts the todo file. GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash <base> accepts the generated plan unchanged, while a sed command can rewrite the verbs programmatically.

open as a page