使用暂存中的未提交文件撤消git reset --hard

我正在努力恢复我的工作。 我愚蠢地做了git reset --hard,但在此之前我只做了get add .而没做git commit.请帮忙! 这是我的日志:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

在这种情况下可以撤消git reset --hard吗?

eistrati asked 2019-07-21T19:29:50Z
8个解决方案
160 votes

您应该能够恢复添加到索引中的任何文件(例如,在您的情况下,使用/),尽管它可能有点工作。 为了将文件添加到索引,git将它添加到对象数据库,这意味着只要尚未发生垃圾收集,它就可以恢复。 在JakubNarębski的答案中有一个如何做到这一点的例子:

  • 执行git reset后恢复添加的文件--hard HEAD ^

但是,我在测试存储库上尝试了这一点,并且有一些问题 - /应该是--cache,我发现它实际上并没有创建.git/lost-found目录。 但是,以下步骤对我有用:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

这应该输出对象数据库中的任何ref,索引或reflog都无法访问的所有对象。 输出看起来像这样:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

......对于每个blob,你可以这样做:

git show 907b308

输出文件的内容。


输出太多了?

更新以回应下面的评论:

如果您发现该命令的输出中列出了许多提交和树,您可能希望从输出中删除任何从未引用的提交引用的对象。 (通常你可以通过reflog回到这些提交 - 我们只对已添加到索引但永远无法通过提交找到的对象感兴趣。)

首先,保存命令的输出,使用:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

现在可以找到那些无法访问的提交的对象名称:

egrep commit all | cut -d ' ' -f 3

因此,您可以找到已添加到索引但未在任何时间提交的树和对象,具有:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

这极大地减少了你必须考虑的物体数量。


更新:下面的Philip Oakley提出了另一种减少要考虑的对象数量的方法,即只考虑/下最近修改过的文件。您可以通过以下方式找到这些文件:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(我在这里发现了/的调用。)该列表的结尾可能如下所示:

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

在这种情况下,您可以看到这些对象:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(请注意,您必须删除路径末尾的/才能获取对象名称。)

Mark Longair answered 2019-07-21T19:31:55Z
59 votes

我只是做了一个git reset --hard并丢失了一个提交。 但我知道提交哈希,所以我能够做git cherry-pick COMMIT_HASH来恢复它。

我在丢失提交的几分钟内就这样做了,所以它可能适合你们中的一些人。

Richard Saunders answered 2019-07-21T19:32:27Z
13 votes

感谢Mark Longair我收回了我的东西!

首先,我将所有哈希保存到文件中:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

接下来我将它们全部放入(删除'无法访问的blob'的东西)列表并将数据全部放入新文件中...你必须选择你的文件并重新命名它们你需要...但我只需要一些 files..hope这有助于某人......

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1
Boy answered 2019-07-21T19:33:06Z
5 votes

在这种情况下,@ Ajedi32在评论中的解决方案对我有用。

git reset --hard @{1}

请注意,所有这些解决方案都依赖于没有git gc,其中一些可能会导致一个,所以在尝试任何事情之前我会压缩.git目录的内容,以便你有一个快照回到如果没有 不适合你。

Duncan McGregor answered 2019-07-21T19:33:38Z
3 votes

遇到同样的问题,但没有将更改添加到索引中。 所以上面的所有命令都没有让我回到理想的变化。

在完成上述所有精心解答之后,这是一个天真的暗示,但可能会像我一样拯救那些没有想到它的人。

在绝望中,我试图在我的编辑器(LightTable)中按CTRL-Z,在每个打开的选项卡中按一次 - 这幸运地恢复了该选项卡中的文件,恢复到git reset --hard之前的最新状态.HTH。

olange answered 2019-07-21T19:34:17Z
1 votes

天哪,我拉了我的头发,直到我遇到这个问题及其答案。 我相信只有你把上面的两条评论放在一起才能得到所问题的正确和简洁的答案,所以这里只有一个地方:

  1. 正如chilicuil所提到的,运行'git reflog'来识别那里提交你想要回复的哈希

  2. 正如akimsko所提到的,你可能不想挑选除非你只丢失一个提交,所以你应该运行'git reset - 硬

关于egit Eclipse用户的注意事项:我找不到在Eclipse中使用egit执行这些步骤的方法。 关闭Eclipse,从终端窗口运行上面的命令,然后重新打开Eclipse对我来说很好。

Lolo answered 2019-07-21T19:35:03Z
1 votes

这对于那里的git专业人士来说可能是显而易见的,但是我想把它放在一边,因为在我的疯狂搜索中我没有看到这个提出来。

我上演了一些文件,并做了一个git reset - hard,吓坏了一点,然后注意到我的状态显示我的所有文件仍然上演以及所有删除未被删除。

此时,只要不进行删除操作,就可以提交这些分阶段的更改。在此之后,你只需要鼓起勇气再一次做“git reset --hard”,这将带你回到那些已经上演但现在刚刚承诺的变化。

同样,这对大多数人来说可能并不是什么新鲜事,但我希望,因为它帮助了我,而且我没有找到任何暗示这一点的东西,它可能会帮助其他人。

ddoty answered 2019-07-21T19:35:50Z
0 votes

上述解决方案可能有效,但是,有更简单的方法可以从中恢复这不是通过earlier 20m-s复杂撤消。 我猜最多git-resets发生在少量文件上,如果你已经使用了VIM,那么可能是最节省时间的解决方案。 需要注意的是,你必须这样做使用git hard --reset persistent-undo,你应该使用哪种方式,因为它使您能够撤消无限数量的更改。

以下是步骤:

  1. 在vim中按earlier 20m并输入命令git hard --reset.如果您的.vimrc中打开了find,它将显示类似于undodir=~/.vim_runtime/temp_dirs/undodir

  2. 在您的仓库中使用earlier 20m查找您上次的最后日期/时间承诺

  3. 在你的shell中导航到你的earlier 20mgit hard --reset

  4. 在此目录中使用此命令查找您拥有的所有文件自上次提交以来已更改

    earlier 20m

    这里“2018-03-20 11:24:44”是最后一次提交的日期和时间。 如果您执行earlier 20m的日期是“2018-03-22”,然后使用“2018-03-22”,然后使用“2018-03-23”。 这是因为找到了一个怪癖,其中下边界是包含的,上限是排他的。[https://unix.stackexchange.com/a/70404/242983]

  5. 接下来转到每个文件在vim中打开它们,并执行“早期20米”。您可以使用“h early”找到有关“更早”的更多详细信息。 这里earlier 20m意思是回到20分钟后的文件状态,假设你做了git hard --reset,回来了20分钟。 重复此过程从find命令吐出的所有文件。 我确定有人可以编写一个结合这些东西的脚本。

alpha_989 answered 2019-07-21T19:37:07Z
translate from https://stackoverflow.com:/questions/7374069/undo-git-reset-hard-with-uncommitted-files-in-the-staging-area