mkrieger1 的答案是正确的(并且被赞成),但是你现在有一个情况,所以需要更多——可能是——git reset @{1}首先。这是解释。
当您运行时,Git从索引(您现在正在构建的下一个提交)和工作树中删除了给定的内容。git rm pathpath
当您随后运行git reset HEAD时,Git 进行了两项更改,其中第一项是空操作。但是随后您运行了第二个 git reset也进行了两个更改,而这一次第一个更改不是空操作。
git reset默认情况下做什么
无论当前分支是什么,它的提交都会更改为HEAD提交。也就是说,如果你在 branch 上,Git通过读取master解析到某个提交 ID ,然后将它刚刚生成的提交 ID 写入. 显然,读取 ID,然后将其原封不动地写回,使其保持不变。所以这有点愚蠢,但这样做是因为如果你说,例如,Git 会转换为一个提交 ID,然后将该提交 ID 写入- 这就是它为你的第二个命令所做的。HEADmastermastergit reset HEAD~1HEAD~1master
调整当前分支后,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指向mastercommit B,留下C悬空:
...--A--B <-- master
\
C
要C返回,如果您知道它的提交 ID,您将运行:
git reset <commit-id>
这将告诉 Git 指向当前分支 ( master) 再次提交C:
...--A--B
\
C <-- master
棘手的部分是:在哪里可以找到 commit 的 ID C?答案是使用reflogs。有一个HEADreflog,每个分支都有一个 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 HEADgit 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的从索引复制到工作树。