用过git reset --hard,现在想撤消它 [英] Used git reset --hard, now wants to undo it

查看:54
本文介绍了用过git reset --hard,现在想撤消它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是错误地使用了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.txtbar.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 added 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆