撤消 git 重置——对于暂存区域中的未提交文件很难

我正在努力恢复我的工作。我愚蠢地做了 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吗?

70144 次浏览

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

然而,我在一个测试存储库中尝试了这种方法,但是有几个问题—— --cached应该是 --cache,而且我发现它实际上并没有创建 .git/lost-found目录。然而,以下步骤对我很有效:

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

它应该输出对象数据库中任何 ref、索引或 reflog 都无法访问的所有对象。输出如下:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

对于这些斑点,你可以做:

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)

这大大减少了需要考虑的对象的数量。


更新: 下面的 菲利普 · 奥克利建议了另一种减少需要考虑的对象数量的方法,即只考虑 .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

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

我刚刚做了一个 git reset --hard,失去了一个提交。但是我知道提交散列,所以我能够执行 git cherry-pick COMMIT_HASH来恢复它。

我在失去提交的几分钟内完成了这个操作,所以它可能对你们中的一些人有用。

多亏了 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

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

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

  2. 正如阿基姆斯科提到的,你可能不会想要挑选 除非您只丢失了一次提交,否则应该运行 git reset --hard <hash-commit-you-want>

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

@ Ajedi32的解决方案在这种情况下对我很有效。

git reset --hard @{1}

注意,所有这些解决方案都依赖于没有 git gc,其中一些可能会导致 gc,因此我将压缩。在尝试任何事情之前,git 目录,这样,如果一个快照不能为您工作,您就可以返回到该快照。

遇到了同样的问题,但没有增加指数的变化。所以上面的所有命令都没有带回我想要的更改。

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

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

上述解决方案可能有效,但是,有更简单的方法可以从中恢复 而不是通过 git-s 复合撤消。我猜大多数情况下 Git-resets 发生在少量文件上,如果您已经使用了 VIM,那么这个 可能是最节省时间的解决方案 应该使用 ViM's持久撤消,这两种方式都应该使用,因为 它为您提供了撤消无限次更改的能力。

以下是步骤:

  1. 在 vim 中按 :并键入命令 set undodir 撤消 在 .vimrc中打开,它将显示类似于 undodir=~/.vim_runtime/temp_dirs/undodir.

  2. 在你的回购使用 git log找出最后的日期/时间,你做了最后一次 提交

  3. 在 shell 中,使用以下命令导航到 undodir cd ~/.vim_runtime/temp_dirs/undodir.

  4. 在此目录中,使用此命令查找您拥有的所有文件 自上次提交以来发生了变化

    - newermt“2018-03-2011:24:44”!-newermt“2018-03-23”(- type f - regextype posx-tended-regex’. *’)-not-path“ */env/*”-not-path “ */. git/*”

    这里的“2018-03-2011:24:44”是最后一次提交的日期和时间 你填写 git reset --hard的日期是“2018-03-22”,然后使用 “2018-03-22”,然后使用“2018-03-23”。这是因为一个奇怪的发现, 下界是包含的,上界是独占的。 Https://unix.stackexchange.com/a/70404/242983

  5. 下一步去每个文件打开他们在 vim,并做一个“早期20m”。 你可以通过使用“ h morning”找到更多关于“ Early”的细节 意思是回到20分钟前文件的状态, 假设你做了 git hard --reset,20分钟回来。重复这个过程 从 find命令中吐出的所有文件。我确定 有人可以写一个脚本把这些东西结合起来

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

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

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

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

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

Edit -> reload from disk

幸运的是,我刚刚做了一个 git status的权利之前,我清除了我的工作变化,所以我确切地知道我必须重新加载。

记住你的文件层次结构和使用技术的马克朗盖尔与菲尔奥克利修改产生戏剧性的结果。

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

哈!

如果你最近检查了一个 git diff,那么还有另一种方法可以从这样的事件中恢复过来,即使你还没有进行更改: 如果 git diff的输出仍然在你的控制台缓冲区中,你只需要向上滚动,将 diff 复制粘贴到一个文件中,然后使用 patch工具将 diff 应用到你的树中: patch -p0 < file。这种方法救了我好几次。

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

我无法使用 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

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