Reputation: 100446
I accidentally ran git rm file
to undo that I ran git reset HEAD
which didn't restore the file, so then I ran git reset HEAD~1
which didn't seem to restore the file either (this should be easy...)
Now I see this:
Unstaged changes after reset:
M test/.suman/logs/runner-debug.log
M test/.suman/logs/test-debug.log
M test/.suman/logs/test-output.log
M test/.suman/logs/tests/test.test-src.alphabet.test.js.log
M test/.suman/logs/tests/test.test-src.four.test.js.log
M test/.suman/logs/tests/test.test-src.one.test.js.log
M test/.suman/logs/tests/test.test-src.three.test.js.log
M test/.suman/logs/tests/test.test-src.two.test.js.log
M test/example.js
D test/test-src/one.test.js
D test/test-src/test-sort.js
M test/test-src/z.test.js
what I am trying to do is restore
D test/test-src/one.test.js
how can I do that?
Upvotes: 0
Views: 367
Reputation: 489928
mkrieger1's answer is correct (and upvoted), but you have a Situation now, so more is required—probably, git reset @{1}
—first. Here's the explanation.
When you ran git rm path
, Git removed the given path
from both the index (the next commit you're building now) and the work-tree.
When you subsequently ran git reset HEAD
, Git made two changes, the first one of which was a no-op. But then you ran a second git reset
that also made two changes, and this time the first change was not a no-op.
git reset
does by defaultWhatever the current branch is, its commit is changed to the HEAD
commit. That is, if you're on branch master
, Git resolves HEAD
to some commit ID by reading master
, then writes the commit ID it just produced into master
. Obviously, reading the ID, then writing it back unchanged, leaves it unchanged. So this is kind of dumb, but is done because if you said git reset HEAD~1
for instance, Git would convert HEAD~1
to a commit ID, then write that commit ID into master
—which is what it did for your second command.
Once the current branch is adjusted, HEAD
now resolves to the new commit ID. So for your first reset
command, HEAD
did not change at all, and for your second one, HEAD
stepped back one commit.
Then, since you let git reset
do a --mixed
(default) reset, Git copies all the files from the adjusted HEAD
into the index.
This second step is part of what you wanted, when you were undoing your git rm
. Your git rm
removed the file from the index and from the work-tree. Your git reset HEAD
put the file back into the index, first "changing" HEAD
to itself in the process. But the file is still missing from your work-tree—and then you did the second git reset
, which made things bad,
Here's a crude text drawing of what you had before the two git reset
s, in terms of the commit graph, all assuming you're on branch master
:
...--A--B--C <-- master
The first git reset
moved master
to point to commit C
. It already pointed to C
so that's no change. But, the second git reset
moved master
to point to commit B
, leaving C
dangling:
...--A--B <-- master
\
C
To get C
back, if you knew its commit ID, you would run:
git reset <commit-id>
This would tell Git to point the current branch (master
) to commit C
again:
...--A--B
\
C <-- master
The tricky part is: where can you find the ID of commit C
? The answer is to use the reflogs. There's one reflog for HEAD
, and one reflog for each branch. The one for HEAD
gets a new entry every time you do something that changes which commit HEAD
points to, and the one for the branch gets a new entry every time you change which commit the branch-name points to. Since git reset
adjusts both, they'll be found in both reflogs, probably as HEAD@{1}
and @{1}
. (If you have done some additional reset
s you may need @{2}
or higher. Also, be sure your shell does not eat the braces—some do; if yours does, you may need to use, e.g., "@{1}"
.)
Hence:
git reset @{1}
or perhaps some higher number, perhaps with some extra quotes. This should get your branch pointed back to the correct commit, and fill in the index. (Or, you can use the raw hash ID, if you have that. Git takes either hash IDs, or anything that converts to a hash ID. To see what hash ID something converts to, use git rev-parse
. Try git rev-parse HEAD
, for instance, and git rev-parse master
.)
Now that you have the branch pointed back to the correct commit—that is, now that you're back to:
...--A--B--C <-- master
or similar—the git reset
, doing a --mixed
reset, still leaves the desired file stored in the index, but not in the work-tree.
There are several ways to get the file back, but the simplest is to copy it from the index to the work-tree:
git checkout -- path
You can also use this, even if you hadn't run any git reset
commands at all:
git checkout HEAD -- path
The key difference is that this version says: copy the given path
from the HEAD
commit into the index, then copy the same path
from the index to the work-tree.
Upvotes: 1
Reputation: 23261
You were correct in running git reset HEAD
first. This un-marked the file from deletion, after which it counts as a change in the working directory.
It is always helpful to run git status
:
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
To restore the file, i.e. to discard the change of the content of the file being deleted:
$ git checkout -- test/test-src/one.test.js
Upvotes: 2