To discard the index without touching the working tree, you can just do this:
git reset
That is what it does. This is equivalent to your self answer, "using git commands only". The end effect is exactly like squirreling away the results of git diff HEAD
, rolling everything back to the topmost commit, and then re-applying that diff to HEAD
to re-create the original work tree.
But suppose you want to throw away the changes from the index such that these discarded changes are actually subtracted from your work. In that case, what I would do would be to use interactive rebase to splice the changes out:
git commit # commit the index
git commit -a # commit the remaining changes
git rebase --interactive HEAD^^ # rebase top two commits onto same branch
In the editor window that comes up, I would simply delete the commit which holds the unwanted changes that came from the unwanted index, and then save and quit, letting the interactive rebase do the rest. There might be merge conflicts, which would have to be resolved, since the previously unstaged wanted changes might have depended on the staged unwanted changes.
At the end, I would have a single commit which has just the changes that were previously unstaged, and the unwanted changes are gone. At that point, I could get rid of that commit, turning it into unstaged changes:
git reset HEAD^
or else just keep the commit and work with it by doing more hacking and git commit --amend
to add to it and update its message.
Everything that rebase --interactive
does (and in fact, that non-interactive rebase does) can be done with cherry-pick
commands, but it provides a convenient "workflow automation".
For any scenarios involving the re-ordering of commits, deletion of commits, squashing of multiple commits into one, rewriting commit messages (other than in the topmost patch) and that sort of thing, it's good to know how to use interactive rebase.
The idea is to turn the representation of all your changes into separate commits, then work with the commit-munging kung-fu, and finally "de-commit" something, if necessary.
For doing complex things, git is better at working with commits than with unstaged changes or the index, which are sort of temporary areas; so you want to get your work out of there and into commits first. In fact, if you have unstaged or staged changes, git prevents you from doing various useful things.
One reason to get your work into commits is that in git is very easy to lose the working copy or the index forever. The working copy and index are not commit objects and so they are not recoverable from the reflog. Type the wrong command that wipes out either or both of these and they are gone for good. But any changes that are commits are recoverable even if you lose them, via the reflog, or via branch pointers or tags that you can set.