123

我正在努力恢复我的工作。我愚蠢地做了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在这种情况下可以撤消吗?

4

13 回答 13

193

您应该能够恢复您添加到索引中的任何文件(例如,在您的情况下,使用git add .),尽管这可能需要一些工作。为了将文件添加到索引中,git 将其添加到对象数据库中,这意味着只要尚未发生垃圾回收,它就可以恢复。Jakub Narębski 在此处的回答中给出了如何执行此操作的示例:

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

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

这应该输出对象数据库中任何引用、索引或引用日志都无法访问的所有对象。输出将如下所示:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

...对于每个 blob,您可以执行以下操作:

git show 907b308

输出文件的内容。


输出太多?

针对以下sehe的评论进行更新:

如果您发现该命令的输出中列出了许多提交和树,您可能希望从输出中删除从未引用的提交中引用的任何对象。(通常,无论如何,您都可以通过 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提出了另一种减少要考虑的对象数量的方法,即只考虑.git/objects. 您可以通过以下方式找到这些:

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

(我在这里find找到了该调用。)该列表的末尾可能如下所示:

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

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

于 2011-09-11T07:44:37.950 回答
64

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

我在丢失提交后的几分钟内完成了此操作,因此它可能对你们中的一些人有用。

于 2013-08-27T17:42:44.817 回答
18

感谢 Mark Longair,我找回了我的东西!

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

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

接下来,我将它们全部(删除“无法访问的 blob”的东西)放在一个列表中,并将数据全部放在新文件中......你必须选择你的文件并再次重命名它们,你需要......但我只需要一些文件..希望这可以帮助某人...

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
于 2014-01-08T13:52:01.467 回答
6

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

git reset --hard @{1}

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

于 2016-01-11T19:14:36.763 回答
4

遇到同样的问题,但没有将更改添加到索引中。所以上面的所有命令都没有给我带来想要的改变。

在上述所有详尽的答案之后,这是一个幼稚的提示,但可能会像我一样拯救那些没有首先想到它的人。

绝望中,我尝试在我的编辑器 (LightTable) 中按 CTRL-Z,在每个打开的选项卡中按一次——幸运的是,这使该选项卡中的文件恢复到git reset --hard. HTH。

于 2016-09-30T10:59:49.367 回答
2

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

  1. 如 chilicuil 所述,运行git reflog以在其中识别您想要返回的提交哈希

  2. 正如 akimsko 所提到的,除非您只丢失一次提交,否则您可能不想选择樱桃,因此您应该运行git reset --hard <hash-commit-you-want>

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

于 2014-12-03T05:50:46.690 回答
2

如果您使用 IDE 编辑文件,它也可能保持自己的历史独立于 Git。在某些情况下,完全绕过 Git 并使用 IDE 要简单得多。例如,IntelliJ IDEA 和 Eclipse 都有这种自动化的本地版本控制,他们称之为“本地历史”。在 IntelliJ 中,检索多个文件中丢失的一整批更改非常简单:在 Project 窗格中,您可以右键单击整个项目或目录树,然后在 Local History 子菜单中选择 Show History。git reset --hard应该在列表中显示为“外部更改”(即从 IDE 外部触发),您可以使用恢复按钮或上下文菜单项将所有内容恢复到外部更改发生之前的状态。

于 2020-08-26T04:11:09.453 回答
1

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

我暂存了一些文件,然后做了一个git reset --hard,有点吓坏了,然后注意到我的状态显示我的所有文件仍然暂存以及所有删除的内容都未暂存。

此时您可以提交这些暂存的更改,只要您不暂存它们的删除。在这之后,你只需要鼓起勇气再做git reset --hard一次,这会让你回到你已经上演的那些变化,现在刚刚承诺。

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

于 2018-12-12T13:43:08.743 回答
1

如果您最近查看过 agit diff那么还有另一种方法可以从此类事件中恢复,即使您尚未暂存更改:如果 的输出git diff仍在您的控制台缓冲区中,您可以向上滚动,复制粘贴diff 到一个文件中并使用该patch工具将 diff 应用于您的树:patch -p0 < file. 这种方法救了我几次。

于 2020-08-07T12:07:52.733 回答
0

上述解决方案可能有效,但是,有更简单的方法可以从中恢复,而不是通过git-s 复杂的撤消。我猜大多数 git-reset 发生在少数文件上,如果你已经使用 VIM,这可能是最省时的解决方案。需要注意的是,您已经必须使用ViM'spersistent-undo,无论哪种方式都应该使用它,因为它使您能够撤消无限数量的更改。

以下是步骤:

  1. 在 vim 中按下:并输入命令set undodir。如果您已persistent undo在您的中打开.vimrc,它将显示类似于 的结果 undodir=~/.vim_runtime/temp_dirs/undodir

  2. 在您的存储库git log中,用于找出您最后一次提交的最后日期/时间

  3. 在您的外壳中导航到您的undodirusing cd ~/.vim_runtime/temp_dirs/undodir

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

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    这里的“2018-03-20 11:24:44”是最后一次提交的日期和时间。如果您执行的日期git reset --hard是“2018-03-22”,则使用“2018-03-22”,然后使用“2018-03-23”。这是因为 find 的一个怪癖,其中下界是包含的,而上限是排除的。 https://unix.stackexchange.com/a/70404/242983

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

于 2018-03-21T16:56:22.713 回答
0

我正在使用 IntelliJ,并且能够简单地浏览每个文件并执行以下操作:

Edit -> reload from disk

幸运的是,在我清除我的工作更改之前,我刚刚完成了一次git status,所以我确切地知道我必须重新加载什么。

于 2019-05-07T18:09:33.340 回答
0

记住您的文件层次结构并使用 Mark Longair 和 Phil Oakley 修改的技术会产生显着的结果。

本质上,如果您至少将文件添加到 repo 但未提交,则可以通过交互使用来恢复git show,检查日志并使用 shell 重定向来创建每个文件(记住您感兴趣的路径)。

于 2019-09-27T20:33:01.063 回答
0

我无法使用 git 命令恢复文件。就我而言,它是一个 CPP 文件。我知道其中有一个独特的字符串,很有帮助。命令是:

sudo grep -a -B[number of rows before the text being searched] -A[number of rows after the text being searched] '[some unique text in the lost file]' /dev/sda3 > test.cpp

搜索整个磁盘,但最终找到了丢失文件的内容。

于 2020-09-01T09:14:39.383 回答