mkrieger1 的答案是正确的(并且被赞成),但是你现在有一个情况,所以需要更多——可能是——git reset @{1}
首先。这是解释。
当您运行时,Git从索引(您现在正在构建的下一个提交)和工作树中删除了给定的内容。git rm path
path
当您随后运行git reset HEAD
时,Git 进行了两项更改,其中第一项是空操作。但是随后您运行了第二个 git reset
也进行了两个更改,而这一次第一个更改不是空操作。
git reset
默认情况下做什么
无论当前分支是什么,它的提交都会更改为HEAD
提交。也就是说,如果你在 branch 上,Git通过读取master
解析到某个提交 ID ,然后将它刚刚生成的提交 ID 写入. 显然,读取 ID,然后将其原封不动地写回,使其保持不变。所以这有点愚蠢,但这样做是因为如果你说,例如,Git 会转换为一个提交 ID,然后将该提交 ID 写入- 这就是它为你的第二个命令所做的。HEAD
master
master
git reset HEAD~1
HEAD~1
master
调整当前分支后,HEAD
现在解析为新的提交 ID。因此,对于您的第一个reset
命令,HEAD
根本没有更改,而对于您的第二个命令,HEAD
则退回了一次提交。
然后,由于您允许git reset
进行--mixed
(默认)重置,Git 会将所有文件从调整后的文件复制HEAD
到索引中。
当您撤消git rm
. 您git rm
从索引和工作树中删除了该文件。您git reset HEAD
将文件放回索引中,首先HEAD
在此过程中“更改”为自身。但是您的工作树中仍然缺少该文件-然后您执行了第二个git reset
,这使事情变得很糟糕,
恢复大部分_
就提交图而言,这是您在两个 s 之前所拥有的粗略文本git reset
图,所有这些都假设您在分支上master
:
...--A--B--C <-- master
第一个git reset
移动master
到指向 commit C
。它已经指出,C
所以没有改变。但是,第二个 git reset
指向master
commit B
,留下C
悬空:
...--A--B <-- master
\
C
要C
返回,如果您知道它的提交 ID,您将运行:
git reset <commit-id>
这将告诉 Git 指向当前分支 ( master
) 再次提交C
:
...--A--B
\
C <-- master
棘手的部分是:在哪里可以找到 commit 的 ID C
?答案是使用reflogs。有一个HEAD
reflog,每个分支都有一个 reflog。HEAD
每次您执行更改提交HEAD
指向的操作时,for 都会获得一个新条目,而分支名称每次更改分支名称指向的提交时都会获得一个新条目。由于git reset
两者都进行了调整,因此它们会在两个 reflogs 中找到,可能是HEAD@{1}
和@{1}
。(如果你做了一些额外reset
的 s 你可能需要@{2}
或更高。另外,确保你的壳不吃大括号——有些吃;如果你吃,你可能需要使用,例如,"@{1}"
。)
因此:
git reset @{1}
或者可能是一些更高的数字,可能还有一些额外的引号。这应该让您的分支指向正确的提交,并填写索引。(或者,您可以使用原始哈希 ID,如果有的话。Git 采用哈希 ID 或任何转换为哈希 ID 的内容。要查看转换为什么哈希 ID,请使用。例如,git rev-parse
尝试和。)git rev-parse HEAD
git rev-parse master
剩下的路
现在您已经将分支指向了正确的提交——也就是说,现在您回到了:
...--A--B--C <-- master
或类似的 -git reset
进行--mixed
重置后,仍将所需文件存储在index中,但不在work-tree中。
有几种方法可以取回文件,但最简单的是将它从索引复制到工作树:
git checkout -- path
即使您根本没有运行任何 git reset
命令,您也可以使用它:
git checkout HEAD -- path
关键区别在于这个版本说:将提交中的给定复制path
到HEAD
索引中,然后将相同path
的从索引复制到工作树。