TL;DR: git rebase -i, edit, git reset HEAD~ and create new commits
When you squashed too much and want to split a git commit into several ones. More wordy explanation on StackOverflow, shorter version here:
-
Use
git rebase -i ...
-
Mark desired commit to be
e
dited -
Rebase will now pause after this commit
-
Run
git reset HEAD~
to cancel the commit -
Use your favourite tool(s) to create new commits
-
When finished, run
git rebase --continue
Note that you should commit all changes!
Also note that this approach deletes old commit and creates new ones, so date and author will be reset.
Alternatively, to move a change from a commit into a new commit (for example, adding a sub-feature to a new feature), you can:
-
Use
git rebase -i ...
-
Mark desired commit to be
e
dited -
Rebase will now pause after this commit
-
Edit file(s) to reverse the change (in our example, remove the sub-feature)
-
Create a (temporary) commit, call it "reverse sub-feature", for example
-
Immediately after that, run
git revert HEAD
-
It will create a new commit, and call it "Revert: reverse sub-feature" - cancel the double-negative and reword it to "add sub-feature"
-
Run
git rebase --continue
to finish the rebase -
Use
git rebase -i ...
again, to fixup your temporary commit ("reverse sub-feature" in our example) into the main commit
And then you'll have your main commit without the sub-feature, followed by a commit adding the sub-feature - exactly what you wanted!
Also, as a bonus - one-liner to create individual commits for each modified file:
git status --porcelain | sed 's/^...//' | xargs -I% sh -c 'git add %; git commit -m "cleanup %" '