使用git reset --hard,现在要撤消它

时间:2016-05-07 19:54:16

标签: git

我只是错误地使用了git reset --hard。现在我要撤消此命令并将我的更改恢复。我怎么能这样做?

我使用git reflog命令并查看以下内容。

6eea708 HEAD@{0}: commit (initial): Initial commit

如何更改我的更改?

3 个答案:

答案 0 :(得分:4)

你很幸运,因为你发表了评论:

  

问题是我没有提交这些更改。 我先做git add . ,然后......

首先,让我们注意git add .分阶段工作树内容。也就是说,假设您当前目录.包含文件foo.txtbar.xml,它们都在当前(HEAD)提交, 1 中,并且您拥有修改其中一个或两个。 add步骤将新内容复制到底层存储库,然后使index / staging-area引用新内容。

This answer位于linked question,所以这在技术上是重复的,但我认为它值得扩展,特别是因为一个答案被埋没相对较远,在下面使用git reset来放弃提交的更常见情况。

  

...我使用git reset --hard

请注意,reset 这种形式不会丢弃任何提交,但 会清除已暂存的工作树文件。也就是说,它不仅可以取消暂存文件 - 即撤消git add和/或git rm - 就像git reset --mixed一样, 会覆盖工作 - 文件的树版本,其中包含已提交的版本。 2

这使得很难恢复分阶段版本,但并非不可能。

这里的关键是文件的内容在存储库中,存储在他们的"哈希名称" - 那些丑陋的SHA-1哈希之类的fa49b077972391ad58037050f2a75f74e3671e92

如果你跑:

$ git fsck

Git会找到任何没有引用它们的哈希名称:

Checking object directories: 100% (256/256), done.
dangling blob fa49b077972391ad58037050f2a75f74e3671e92

用于引用此文件的索引/暂存区域,但git reset --hard清除了该文件。所以现在没有任何内容涉及这个fa49b...文件。

您可以使用git plumbing命令提取文件,但是如果有很多这些命令并且您希望更容易引用它们,则可以将--lost-found选项添加到git fsck,然后查看在丢失和找到的目录中,这些"悬空blob"文件已被提取:

$ 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
$ 

(我只是添加,然后重置,一个文件;如果有很多文件,你将不得不筛选它们以找到任何好文件。)

当您完成根据丢失的bin内容进行生根时,删除它们(cd .git/lost-found && rm -rf *)可能是一个好主意,尽管它们离开那里基本上是无害的 - 它们只占用空间。您可以稍后再次运行git fsck --lost-found以重新填充它(使用任何当时悬挂的提交和blob)。

请注意,如果等待时间过长,此方法将失败 - 这些悬空blob对象不会像丢弃提交那样受到半保护(通过reflog条目)。但是,默认情况下,剩余的对象无论如何都不会被修剪14天。这意味着您有两周的时间,而不是提交时间约一个月。

这里还有一个陷阱,并不会影响实践中的人。我重置的文件有一行读取new file(加上换行符)。假设,无论出于何种原因,某些其他文件在某些​​现有(未丢弃)提交中具有完全相同的内容。当我git add编辑文件时,Git会计算出相同的"真名称" hash- 任何文件的真实名称,只包含一行new file,但始终为fa49b077972391ad58037050f2a75f74e3671e92,在地球上任何地方的任何存储库 3 - 并且只会引用该文件的现有副本。重置它并运行git fsck,Git不会注意到悬空blob,因为此时没有悬空:某些其他提交引用文件{{ 1}},也许在相同的路径名下,或者可能在其他路径下。

1 它还添加了不在fa49b077972391ad58037050f2a75f74e3671e92提交中的文件,但我不想涉及担心HEAD指令的所有方面问题。见脚注2: - )

2 如果文件是新添加的,.gitignore仍会从工作树中删除该文件。这就是git reset --hard复杂功能的来源:如果文件被忽略,则不会添加,但是.gitignore也没有将其删除。如果 已添加,它将存储在存储库中,我们将返回git reset --hard"中存在的"文件。案件。所以这一切都出现在洗涤中。

3 这表明攻击Git存储库的方法,包括使用Google作为大规模索引引擎:搜索blob哈希,直到找到重复的哈希 H ,但是指的是两个不同的内容 C 1 C 2 。然后,知道有人想要使用存储库 R (它没有该blob的版本),以便将来保存内容 C 1 ,在 R 中提交内容 C 2 。没有 new 提交现在可以包含内容 C 1 ,因为Git会将其哈希到 H 并确定它已经是在存储库中。

实际上,这种攻击不是。 : - )

答案 1 :(得分:1)

你可以挑选一个你想要的提交

git cherry-pick <commit_id>

答案 2 :(得分:1)

根据您的git reflog输出,您可以执行

git reset HEAD@{0}

git reset 6eea708