我只是错误地使用了git reset --hard。现在我要撤消此命令并将我的更改恢复。我怎么能这样做?
我使用git reflog
命令并查看以下内容。
6eea708 HEAD@{0}: commit (initial): Initial commit
如何更改我的更改?
答案 0 :(得分:4)
你很幸运,因为你发表了评论:
问题是我没有提交这些更改。 我先做
git add .
,然后......
首先,让我们注意git add .
分阶段工作树内容。也就是说,假设您当前目录.
包含文件foo.txt
和bar.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