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

时间:2011-09-10 19:29:00

标签: git undo git-commit git-reset git-add

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

13 个答案:

答案 0 :(得分:160)

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

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

.git/lost-found

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

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

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

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

输出文件内容。


输出太多了?

更新以回复以下sehe的评论:

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

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

git show 907b308

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

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

因此,您只能找到已添加到索引中但未在任何时间提交的树和对象:

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

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


下面的

更新: Philip Oakley提出了另一种减少要考虑的对象数量的方法,即只考虑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 调用here。)该列表的结尾可能如下所示:

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 才能获取对象名称。)

答案 1 :(得分:59)

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

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

答案 2 :(得分:13)

感谢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

答案 3 :(得分:5)

@ Ajedi32在评论中的解决方案正是在这种情况下为我工作的。

git reset --hard @{1}

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

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:2)

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

答案 6 :(得分:1)

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

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

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

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

答案 7 :(得分:1)

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

我暂存了一些文件,并进行了git reset --hard硬操作,将其吓坏了一点,然后注意到我的状态显示我的所有文件仍在暂存中,而所有删除都未暂存。

在这一点上,您可以提交那些已分阶段的更改,只要您不对它们进行删除即可。 在此之后,您只需要鼓起勇气再进行一次“ git reset --hard”操作,这将使您重新回到您已上演但现在刚刚执行的更改。

再次,对大多数人来说,这可能并不是什么新鲜事,但是我希望它能对我有所帮助,但我没有发现任何暗示,它可能会对其他人有所帮助。

答案 8 :(得分:1)

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

答案 9 :(得分:0)

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

以下是步骤:

  1. 在vim中按:并输入命令set undodir。如果您在persistent undo中启用.vimrc,则会显示类似的结果 undodir=~/.vim_runtime/temp_dirs/undodir

  2. 在您的回购中使用git log找出您上次的最后日期/时间 提交

  3. 在您的shell中导航至undodir使用 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/*"

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

  5. 接下来转到每个文件在vim中打开它们,然后做一个早期的20m&#34;。 你可以找到关于&#34;更早的&#34;的更多细节。通过使用&#34; h更早&#34;。这里 earlier 20m意味着回到20分钟后的文件状态, 假设你做了git hard --reset,回来了20分钟。重复此过程 从find命令中吐出的所有文件。我确定 有人可以编写一个结合这些东西的脚本。

答案 10 :(得分:0)

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

Edit -> reload from disk

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

答案 11 :(得分:0)

记住您的文件层次结构,并使用Mark Longair的技术对Phil Oakley进行修改,会产生引人注目的结果。

基本上,如果您至少将文件添加到了仓库中但未提交,则可以使用“ git show”进行交互来还原,检查日志并使用Shell重定向来创建每个文件(记住您的关注路径)。 / p>

HTH!

答案 12 :(得分:0)

我无法使用git命令恢复文件。就我而言,这是一个CPP文件。我知道其中有一个唯一的字符串,this提供了帮助。命令是:

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

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