用过git reset --hard,现在想撤消它 [英] Used git reset --hard, now wants to undo it
问题描述
我只是错误地使用了git reset --hard.现在,我想撤消该命令并将所做的更改恢复.我该怎么办?
I just by mistake used git reset --hard. Now I want to undo this command and bring my changes back. How can I do this?
我使用了git reflog
命令,请参见以下内容.
I used git reflog
command and see following.
6eea708 HEAD@{0}: commit (initial): Initial commit
如何恢复我的更改?
推荐答案
您很幸运,因为您对此发表了评论:
You are in luck because of this bit you put in a comment:
问题是我没有提交那些更改. 我先做了
git add .
,然后...
The thing is that I did not commit those changes. I first did
git add .
and then ...
首先,让我们注意git add .
暂存工作树内容.也就是说,假设您的当前目录.
包含文件foo.txt
和bar.xml
都在当前(HEAD)提交 1 中,并且您已经修改了其中一个或两个. add
步骤将新内容复制到基础存储库中,然后使索引/临时区域引用新内容.
First, let's note that git add .
stages work-tree contents. That is, suppose that .
, your current directory, contains files foo.txt
and bar.xml
that are both in the current (HEAD) commit,1 and you have modified one or both of these. The add
step copies the new contents into the underlying repository, and then makes the index/staging-area refer to the new contents.
此答案位于
This answer is in the linked question, so this is technically a duplicate, but I think it's worth expanding on a bit, especially since that one answer is buried relatively far down, below the more common case of having used git reset
to discard a commit.
...我用过
git reset --hard
请注意,这种形式的reset
不会丢弃任何提交,但是不会清除已暂存的工作树文件.也就是说,它不仅取消暂存文件(即撤消git add
和/或git rm
),就像git reset --mixed
一样,它 还会覆盖文件的工作树版本以及已提交的版本. 2
Note that this form of reset
does not discard any commits, but it does clear out work-tree files that have been staged. That is, it not only unstages files—i.e., undoes git add
and/or git rm
—just like git reset --mixed
, it also overwrites the work-tree version of the file with the committed version.2
这使得找回暂存版本很困难,但并非没有可能.
This makes it difficult to get the staged version back, but not impossible.
这里的关键是文件的内容 在存储库中,并以其哈希名称"存储-像fa49b077972391ad58037050f2a75f74e3671e92
这样的丑陋的SHA-1哈希之一.
The key here is that the file's contents are in the repository, stored under their "hash name"—one of those big ugly SHA-1 hashes like fa49b077972391ad58037050f2a75f74e3671e92
.
如果您运行:
$ git fsck
Git会找到任何与它们无关的哈希名称:
Git will find any hash-names that have nothing referring to them:
Checking object directories: 100% (256/256), done.
dangling blob fa49b077972391ad58037050f2a75f74e3671e92
用于引用此文件的索引/暂存区,但是git reset --hard
清除了该内容.因此,现在没有任何内容引用此fa49b...
文件.
The index/staging-area used to refer to this file, but git reset --hard
cleared that out. So now nothing refers to this fa49b...
file.
您可以使用git plumbing命令提取文件,但是如果有很多这样的命令并且想要更轻松地引用它们,则可以在git fsck
中添加--lost-found
选项,然后查看丢失的-找到的目录,这些悬挂的Blob"文件已在其中提取:
You can extract the file with git plumbing commands, but if there are a bunch of these and you want to refer to them more easily, you can add the --lost-found
option to git fsck
, then look in the lost-and-found directory, where these "dangling blob" files have been extracted:
$ git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling blob fa49b077972391ad58037050f2a75f74e3671e92
$ cd .git/lost-found/other
$ ls
fa49b077972391ad58037050f2a75f74e3671e92
$ cat fa49b077972391ad58037050f2a75f74e3671e92
new file
$
(我只添加了一个文件,然后重置了该文件;如果有很多文件,则必须将它们全部筛选以找到任何好的文件.)
(I only added, then reset away, the one file; if there were many files you would have to sift through them all to find any good ones.)
当您彻底找到丢失的垃圾箱中的内容时,将它们删除(cd .git/lost-found && rm -rf *
)可能是一个好主意,尽管将它们留在那儿对它们无害,它们只是占据空间.您可以稍后再次运行git fsck --lost-found
来重新填充它(带有任何随后悬挂的提交和Blob).
When you are done rooting through the lost-found bin contents, it is probably a good idea to remove them (cd .git/lost-found && rm -rf *
), although they are largely harmless to leave there—they just occupy space. You can run git fsck --lost-found
again later to repopulate it (with any then-dangling commits and blobs).
请注意,如果等待时间太长,此方法将失败-这些悬空的Blob对象不会像被丢弃的提交那样受到半保护(通过reflog条目).但是,默认情况下,剩余的对象无论如何都不会修剪14天.这意味着您有两周的时间,而不是大约一个月的提交时间.
Note that this method will fail if you wait too long—these dangling blob objects are not semi-protected (by reflog entries) the way discarded commits are. By default, though, leftover objects are not pruned for 14 days anyway. This means you have two weeks, instead of about a month as for commits.
这里还有一个陷阱,并不是说它会在实践中影响人们.我重设的文件只有一行读new file
(加上换行).假设由于某种原因,某些 other 文件在某些现有(未丢弃)提交中的内容完全相同.当我git add
ed该文件时,Git会计算出相同的真实名称"哈希值-在任何内容中,仅包含一行new file
的 any 文件的真实名称始终为fa49b077972391ad58037050f2a75f74e3671e92
,在地球上任何地方的任何存储库 3 -都将引用该文件的现有副本.将其重置并运行git fsck
,Git不会注意到有一个悬空的斑点,因为在这一点上它是不悬空的:一些 other 提交引用了文件fa49b077972391ad58037050f2a75f74e3671e92
,可能使用相同的路径名,也可能使用其他路径.
There is one more trap here, not that it will affect people in practice. The file I reset away had one line reading new file
(plus a newline). Suppose that, for whatever reason, some other file, in some existing (not discarded) commit, has the exact same contents. When I git add
ed the file, Git would have computed this same "true name" hash—the true name of any file containing nothing but the one line new file
is always fa49b077972391ad58037050f2a75f74e3671e92
, in any repository anywhere on the planet3—and would have just referred to the existing copy of that file. Resetting it away and running git fsck
, Git will not notice a dangling blob, because at this point it is not dangling: some other commit refers to file fa49b077972391ad58037050f2a75f74e3671e92
, perhaps under the same path name, or perhaps under some other path.
1 它还会添加不在HEAD
提交中的文件,但我不想陷入担心.gitignore
指令的所有附带问题.参见脚注2::-)
1It also adds files that are not in the HEAD
commit, but I did not want to get into all the side issues of worrying about .gitignore
directives. See footnote 2. :-)
2 如果是新添加的文件,git reset --hard
仍将从工作树中删除该文件.这就是.gitignore
复杂性出现的地方:如果忽略了该文件,则不会添加该文件,但是git reset --hard
也不会将其删除.如果添加了 ,则它位于存储库中,我们回到文件存在于HEAD
中"的情况.因此,无论如何,这一切都是洗出来的.
2If the file is newly added, git reset --hard
still removes the file from the work-tree. This is where the .gitignore
complication comes in: if the file was ignored, it wasn't added, but then git reset --hard
did not remove it either. If it was added, it's in the repository and we are back to the "file existed in HEAD
" case. So this all comes out in the wash anyway.
3 这建议一种对Git存储库的攻击方法,包括使用Google作为大规模索引引擎:搜索blob哈希,直到找到重复的哈希 H 为止,但指的是两个不同的内容 C 1 和 C 2 .然后,知道将来有人会使用没有该blob版本的存储库 R 来保存内容 C 1 ,在 R 中提交内容为 C 2 的提交.现在,任何 new 提交都不能包含内容 C 1 ,因为Git会将其哈希到 H 并确定它已经在存储库中.
3This suggests a method of attack on Git repositories, consisting of using Google as a massive indexing engine: search blob hashes until you find some hash H that repeats, but refers to two different contents C1 and C2. Then, knowing that someone will want to use repository R, which has neither version of that blob, to hold content C1 in the future, make a commit in R holding content C2 instead. No new commit can now contain content C1 since Git will hash it to H and decide that it is already in the repository.
实用,不是这种攻击. :-)
Practical, this attack is not. :-)
这篇关于用过git reset --hard,现在想撤消它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!