I have a commit with many files in it.
One of the files in the commit has several changes, one of which I would like to undo.
So, I was thinking I could reset that particular file back to HEAD~
leaving the outstanding changes I had made in the working-directory. Then, I could use git add -p
and only stage the desired changes, followed by git commit --amend
.
First, I made sure that I had no outstanding changes in either my working-directory or stage.
Then, I ran this command:
git reset HEAD~ -- path/to/file
But, to my surprise, git status
now shows changes to both my working-directory and stage. Further inspection using gitk
shows that the changes in my working-directory and stage are identical.
Where am I going wrong here?
This kind of reset:
HEAD
itself or the current branch (so it's not the same as any of git reset --hard
, git reset --mixed
, or git reset --soft
);Hence this:
So, I was thinking I could reset that particular file back to
HEAD~
leaving the outstanding changes I had made in the working-directory. Then, I could usegit add -p
and only stage the desired changes, followed bygit commit --amend
.
... is a fine plan! But as you saw, the output of git status
becomes a bit surprising:
git status
now shows changes to both my working-directory and stage.
The reason is that what git status
does includes running two git diff
s. If you could name the index and the work-tree as arguments to git diff
(you can't), that might be:
git diff --name-status HEAD <index>
: this is "changes to be committed".git diff --name-status <index> <work-tree>
: this is "changes not staged for commit".Since the index version of path/to/file
now matches the HEAD~1
version of path/to/file
, which is different from the HEAD
version, the first git diff
shows that "staged changes" exist.
Likewise, the work-tree version does not match the index version, so git status
says that there unstaged changes also exist.
You can in fact now run git add -p
. Git will extract the index version to a temporary file, diff the index version against the work-tree version, and let you add changes to the index version (from the work-tree version).
If you wish to be able to see what you are doing, though, it might have been better to, at the start, do a git reset --soft HEAD~1
. This kind of reset:
HEAD
to find out what that branch is and adjust it;You would now be in a similar position as before, but now you would need to:
git reset HEAD path/to/file
to make the index version match the HEAD
version, now that HEAD
names the desired commit. The commit you made that you don't want—that you intended to --amend
—is now "one past the end" of the branch tip:
X [bad commit, now shoved out of the way]
/
...--o--o--@ <-- branch
Now git status
makes sense, because it's comparing @
to index instead of X
to index. (As before, git status
also compares index to work-tree.) Now you can git add -p
as you intended, and then git commit
without the --amend
part.
You do not have to do it this way; you can just continue with your current plan. If you like the git status
result here better, though, you can convert your current plan to this other plan right now, by doing the git reset --soft HEAD~1
. This will change the current branch name so that it stops pointing to X
and starts pointing to @
, and change nothing else at all.