How do you split one commit into two using Git interactive rebase?
answer
- you need the verb that pauses
- the rebase stops with the commit already made
- undo the commit but keep the files
- stage pieces with add -p and commit twice
- mixed reset, then continue
basics
~20 sMark 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.
solid answer
~50 sStart `git rebase -i <base>` and change that commit's line from `pick` to `edit`. Git replays up to and including that commit, then stops with the commit already created and `HEAD` on it. Run `git reset HEAD^` — a mixed reset that moves `HEAD` back one commit and unstages everything, leaving all the changes in your working tree. Now stage the first slice with `git add -p` (or `git add <path>`) and `git commit`, then stage and commit the rest; repeat for as many pieces as you want. Finish with `git rebase --continue` and the remaining commits replay on top. `git reset -p HEAD^` does the unstaging interactively if you prefer to pick hunks while backing out. The end state must contain the same total change — check with a diff against the pre-rebase tip.
code
console · 11 lines$ git rebase -i HEAD~3
Stopped at 4b7e0aa... fix login and rename authenticator
$ git reset HEAD^
Unstaged changes after reset:
M src/auth/Authenticator.java
M src/auth/LoginController.java
$ git add src/auth/Authenticator.java
$ git commit -m "rename authenticator"
$ git add -p src/auth/LoginController.java
$ git commit -m "fix login redirect"
$ git rebase --continuego deeper
Know that the edit verb pauses the rebase on that commit and that git rebase --continue resumes it; the splitting details can come later.
Walk the full sequence and explain why the reset must be mixed: the commit is undone while its changes stay in the working tree for git add -p.
Talk about why you split — reviewable, independently revertable units — and verify with an empty diff against the old tip before rewriting a branch anyone else can see.
Decide when this cleanup is worth engineering time at all, and set the expectation for what a commit should be in a repo where commits are the unit of review and rollback.
## Why this needs the edit verb Every other todo verb produces commits automatically. `edit` is the one that hands control back to you: Git applies the commit, then **pauses the rebase** with that commit made and `HEAD` sitting on it in a detached state. Anything you do at that pause becomes part of the replayed history, and `git rebase --continue` resumes with the remaining todo entries. Splitting is the classic use. You have one commit that does two unrelated things — a bug fix plus a rename, or a feature plus a formatting sweep — and you want two reviewable commits instead. ## The procedure 1. `git rebase -i <base>` where `<base>` is at least one commit before the one you want to split. 2. Change its line from `pick` to `edit` and save. Git stops there. 3. `git reset HEAD^`. This is a **mixed** reset: it moves `HEAD` back to the commit's parent and resets the index to match, but leaves the working tree untouched. The effect is that the commit is undone as a commit while all its changes sit in front of you as unstaged modifications. 4. Stage the first logical slice. `git add -p` walks the changes hunk by hunk so you can take half of a file; `git add <path>` is enough when the split is along file lines. 5. `git commit` with a message describing only that slice. 6. Repeat for the remaining changes until `git status` is clean. 7. `git rebase --continue`. An alternative to step 3 is `git reset -p HEAD^`, which asks per hunk which parts to back out of the index. That is convenient when you would rather remove the second slice from the commit than rebuild both from nothing, but the plain mixed reset plus `git add -p` is the version the Git manual documents and the one to describe in an interview. ## What is actually happening The pause is not magic: during a rebase `HEAD` is detached at the replayed commit, and `.git/rebase-merge/` holds the remaining todo entries. Ordinary commands work normally — `git reset`, `git commit`, `git status`. When you run `--continue`, Git picks up the remaining todo lines and replays them onto whatever `HEAD` now is. That is why creating **two** commits at the pause is legal: the rest of the branch simply lands on top of the second one. Because a commit's SHA hashes its parent, every commit after the split point is rewritten too. The split commit's own original SHA disappears from the branch. ## Verifying you did not lose anything The safety check is a tree comparison, not a log read. Note the branch tip before you start (or recover it from the reflog afterwards) and run `git diff <old-tip> HEAD`. For a pure split the diff must be **empty**: you re-packaged the same total change into more commits. A non-empty diff means a hunk was left unstaged and silently dropped, or you edited content while splitting. A second useful check is `git log --oneline` plus `git show --stat` on each new commit, confirming each one is self-contained. ## Pitfalls - **Forgetting to commit the leftovers.** If you run `git rebase --continue` while changes are still uncommitted, Git refuses or the remaining work gets dragged along in a confusing state. Get `git status` clean first. - **`git reset --hard HEAD^` instead of a mixed reset.** That throws the changes away instead of unstaging them; the whole point is to keep the working tree. - **Splitting a commit whose halves are not independent.** Each intermediate commit ideally builds and tests. If slice one does not compile without slice two, either reorder the slices or accept that the split is artificial. - **Splitting published commits.** Everything from the split point on is rewritten, so a pushed branch diverges and needs a force push — fine on a personal branch, disruptive on a shared one. - **Losing your place.** If the pause becomes confusing, `git rebase --abort` returns the branch to exactly where it started. ## When it is worth it Splitting is cleanup for reviewability: a reviewer reading a 400-line commit called "fix login" that also renames forty files has a bad time, while two commits — "rename authenticator", "fix login redirect" — read in minutes. It also makes each commit an independently revertable unit, which is the practical payoff long after the review is over.
- Why a mixed reset rather than git reset --hard HEAD^ at the pause?A mixed reset moves `HEAD` back and clears the index but leaves the working tree exactly as it was, which is precisely what you need — the commit's changes are still on disk, ready to be staged in pieces. `--hard` would also reset the working tree, deleting the very changes you are trying to redistribute.
- How do you prove the split lost nothing?Diff the trees, not the logs: `git diff <old-tip> HEAD` must be empty, since a split only re-packages the same total change. The old tip comes from the reflog if you did not note it. A non-empty diff usually means a hunk was left unstaged and dropped when you continued.
- Can you create more than two commits at a single edit stop?Yes. The pause is an ordinary detached-HEAD state, so you can commit as many times as you like; the remaining todo entries replay on top of whatever HEAD ends up being. Splitting one commit into three or four is the same procedure repeated until `git status` is clean.
saying these in an interview costs you the question
- Using reset --hard at the pause and losing the changes
- Thinking a commit can be split without stopping the rebase
- Believing you may only make one commit at an edit stop
- Running rebase --continue with changes still uncommitted
- Assuming only the split commit is rewritten, not its descendants