如何撤消 git reset --hard HEAD~1?

Posted

技术标签:

【中文标题】如何撤消 git reset --hard HEAD~1?【英文标题】:How can I undo git reset --hard HEAD~1? 【发布时间】:2010-09-05 13:12:00 【问题描述】:

是否可以撤消由以下命令引起的更改?如果有,怎么做?

git reset --hard HEAD~1

【问题讨论】:

我已经写了一个完整的指南来用 git 恢复任何丢失的提交。它甚至还有插图 :-) [查看][fixLink] [fixLink]: programblings.com/2008/06/07/… --hard 丢弃未提交的更改。由于 git 不跟踪这些,因此无法通过 git 恢复它们。 ***.com/questions/34519665/… This 是一篇很棒的文章,可以帮助您恢复文件。 这是直接来自 Github 的绝佳资源:How to undo (almost) anything with Git 【参考方案1】:

Pat Notz 是正确的。只要提交在几天之内,您就可以取回提交。 git 仅在大约一个月左右后才会收集垃圾,除非您明确告诉它删除较新的 blob。

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@0: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@1: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

您可以在示例中看到 file2 由于硬重置而被删除,但在我通过 reflog 重置时又被放回原处。

【讨论】:

你可以使用“git reset --hard HEAD@1”,不需要使用SHA1。在大多数情况下,使用“git reset --hard ORIG_HEAD”就足够了。 git log -g 可以比git reflog 更好地查看引用日志。 这里有一个非常重要的警告......那就是“--hard”部分。 --hard 清除您本地未提交的更改。而且你不能像这样让他们回来(因为他们没有在任何地方被承诺)。我相信你对此无能为力:( ^ 只是为了让您知道可以在执行重置之前存储本地更改 --hard,然后只需弹出它们,您什么都不会丢失!一定要喜欢 git。 我更喜欢 git reflog 而不是 git log -g 只是因为你可以在一行中获得所有信息,其中 sha1、HEAD 信息和提交消息都排成一行。更容易阅读。【参考方案2】:

您要做的是指定要恢复到的提交的 sha1。您可以通过检查 reflog (git reflog) 获取 sha1,然后执行

git reset --hard <sha1 of desired commit>

但不要等待太久......几周后,git 最终会将该提交视为未引用并删除所有 blob。

【讨论】:

警告 几分钟前我自己:这将重置所有修改。但它不会触及未跟踪的文件。【参考方案3】:

答案隐藏在上面的详细回复中,你可以这样做:

$> git reset --hard HEAD@1

(查看git reflog show的输出

【讨论】:

请注意,如果您在重置后进行了任何其他 repo 更改,这不是解决方案。在运行任何东西之前一定要看看 reflog。 不小心重置了我的存储库,以为我的工作永远丢失了。这个答案拯救了我的一天。 哇!你真的救了我一天=)【参考方案4】:

如果 Git 还没有进行垃圾回收,则可以恢复它。

使用fsck 了解悬空提交的概述:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

使用 rebase 恢复悬空提交:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

【讨论】:

这真是太棒了。挽救了生命。 详细解释可以在这里找到:medium.com/@CarrieGuss/…。救命的东西。【参考方案5】:

据我所知,--hard 将丢弃未提交的更改。因为这些不被 git 跟踪。但您可以撤消discarded commit

$ git reflog

将列出:

b0d059c HEAD@0: reset: moving to HEAD~1
4bac331 HEAD@1: commit: added level introduction....
....

其中4bac331discarded commit

现在只需将头部移至该提交::

$ git reset --hard 4bac331

【讨论】:

你让我免于被解雇【参考方案6】:

如果你真的很幸运,就像我一样,你可以回到你的文本编辑器并点击“撤消”。

我知道这不是一个真正正确的答案,但它为我节省了半天的工作时间,所以希望它对其他人也有同样的效果!

【讨论】:

这实际上是一个非常好的技巧,为我节省了很多时间;)而且它比在 git 中做任何事情都简单... 感谢您在这整个世界上的所有善良。谢谢。谢谢。谢谢。 这是硬重置后恢复文件中未暂存更改的唯一方法。也救了我;) 作为附加提示,一些 IDE 作为 eclipse 还保存了最近的文件历史记录。这样,您甚至可以在编辑器关闭后恢复较旧的更改。这对我来说很神奇。 你拯救了我的一天,你是英雄。【参考方案7】:

在大多数情况下,是的。

根据您在运行命令时存储库所处的状态,git reset --hard 的效果可以从微不足道到撤消,再到基本上不可能。

下面我列出了一系列不同的可能情况,以及如何从这些情况中恢复。

我的所有更改都已提交,但现在提交已消失!

这种情况通常发生在您运行带有参数的git reset 时,例如git reset --hard HEAD~。别担心,这很容易恢复!

如果您只是运行 git reset 并且此后没有做任何其他事情,您可以使用这个单行代码回到原来的位置:

git reset --hard @1

这会重置您的当前分支,无论它在上次修改之前处于何种状态(在您的情况下,对分支的最新修改将是您尝试撤消的硬重置)。

但是,如果您在重置后对您的分支进行了其他修改,则上述单行代码将不起作用。相反,您应该运行git reflog &lt;branchname&gt; 以查看最近对您的分支所做的所有更改(包括重置)的列表。该列表将如下所示:

7c169bd master@0: reset: moving to HEAD~
3ae5027 master@1: commit: Changed file2
7c169bd master@2: commit: Some change
5eb37ca master@3: commit (initial): Initial commit

在此列表中找到您要“撤消”的操作。在上面的示例中,它将是第一行,即“重置:移动到 HEAD~”。然后复制提交before(下)该操作的表示。在我们的例子中,这将是master@1(或3ae5027,它们都代表相同的提交),然后运行git reset --hard &lt;commit&gt; 将您当前的分支重置回该提交。

我使用git add 进行了更改,但从未提交。现在我的更改消失了!

这有点难以恢复。 git 确实 有您添加的文件的副本,但由于这些副本从未绑定到任何特定的提交,您无法一次恢复所有更改。相反,您必须在 git 的数据库中找到各个文件并手动恢复它们。您可以使用git fsck 执行此操作。

详情请见Undo git reset --hard with uncommitted files in the staging area。

我对工作目录中的文件进行了更改,这些更改我从未使用git add 暂存,也从未提交过。现在我的更改消失了!

哦哦。我不想告诉你这个,但你可能不走运。 git 不会存储您未添加或提交的更改,并且根据documentation for git reset

--困难

重置索引和工作树。 &lt;commit&gt; 以来对工作树中跟踪文件的任何更改都将被丢弃。

您可能可能能够使用某种磁盘恢复实用程序或专业的数据恢复服务来恢复您的更改,但在这一点上,这可能比它的价值更麻烦。

【讨论】:

One-liner 为我工作,谢谢,但我只是想知道“@1”到底是做什么的...... @StanB 文档在这里:git-scm.com/docs/git-rev-parse 基本上它是指当前分支上的第一个 reflog 条目。 感谢您涵盖所有案例。我没有提交或添加我的。【参考方案8】:

IRL 案例示例:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <stian@Stians-Mac-mini.local>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

【讨论】:

悬空的 blob 听起来像一个 AD&D 怪物! 谢谢@Stian 解释得很好!我想为其他找到此答案的人补充一下,如果您有多个“悬空”提交,则不确定您是否要在最后一行进行变基:) git show 保存了我的一些文件,非常感谢老兄!【参考方案9】:

git reflog

在列表中找到您的提交 sha,然后将其复制并粘贴到此命令中:

git cherry-pick &lt;the sha&gt;

【讨论】:

git-cherry-pick - 应用一些现有提交引入的更改。我认为在这种情况下它很简单并且很有帮助 每个人在搜索如何撤消硬重置更改时实际上都在寻找这个。这个答案应该得到更多的支持 就是这样,你拯救了我的一天? 效果很好。你救了我的命!【参考方案10】:

如果您还没有对您的存储库进行垃圾收集(例如使用git repack -dgit gc,但请注意垃圾收集也可以自动发生),那么您的提交仍然存在 - 它不再可以通过 HEAD 访问。

您可以尝试通过查看git fsck --lost-found 的输出来找到您的提交。

较新版本的 Git 有一个叫做“reflog”的东西,它记录了对 refs 所做的所有更改(与对存储库内容所做的更改相反)。因此,例如,每次您切换 HEAD(即每次您执行 git checkout 切换分支时)都会被记录。而且,当然,您的 git reset 也操纵了 HEAD,因此它也被记录了。您可以通过使用@ 符号而不是~ (如git reset HEAD@1),以与访问存储库的旧状态类似的方式访问引用的旧状态。

我花了一段时间才明白 HEAD@1 和 HEAD~1 的区别,所以这里稍微解释一下:

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@1 # => Three
git reflog

所以,HEAD~1 的意思是“转到 HEAD 当前指向的提交之前的提交”,而 HEAD@1 的意思是“转到 HEAD 指向的提交之前它指向的当前指向的位置”。

这将让您轻松找到丢失的提交并恢复它。

【讨论】:

另一种我认为更清楚的解释:HEAD~1 表示转到“HEAD 的父级”,而 HEAD@1 表示“返回 HEAD 历史的一步” 问题是“历史”这个词在 VCS 中真的超载了。另一种表达方式是~在提交历史中倒退,而@在时序或时间历史中倒退。但这三个版本都不是特别好。 @kizzx2(和 Jorg)实际上将这 3 个解释放在一起时,会有很大帮助 - thx【参考方案11】:

如果您使用的是 JetBrains IDE(任何基于 IntelliJ 的),您甚至可以通过其“本地历史记录”功能恢复未提交的更改。

右键单击文件树中的***目录,在上下文菜单中找到“本地历史记录”,然后选择“显示历史记录”。这将打开一个视图,您可以在其中找到您最近的编辑,找到要返回的修订后,右键单击它并单击“还原”。

【讨论】:

这个功能对于那些对 git 很马虎的人(比如我自己)来说是一个救命稻草。【参考方案12】:

在回答之前让我们添加一些背景知识,解释一下这是什么HEAD

First of all what is HEAD?

HEAD 只是对当前分支上当前提交(最新)的引用。 在任何给定时间只能有一个HEAD。 (不包括git worktree

HEAD 的内容存储在.git/HEAD 中,它包含当前提交的 40 字节 SHA-1。


detached HEAD

如果您不是最新的提交 - 这意味着 HEAD 指向历史上的先前提交,它称为 detached HEAD

在命令行上它看起来像这样- SHA-1 而不是分支名称,因为 HEAD 没有指向当前分支的尖端


关于如何从分离的 HEAD 中恢复的几个选项:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

这将签出指向所需提交的新分支。 此命令将签出给定的提交。 此时您可以创建一个分支并从这一点开始工作。

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

您也可以随时使用refloggit reflog 将显示更新HEAD 的任何更改,并且检查所需的引用日志条目会将HEAD 设置回此提交。

每次修改 HEAD 时,reflog 中都会有一个新条目

git reflog
git checkout HEAD@...

这会让你回到你想要的提交


git reset HEAD --hard &lt;commit_id&gt;

“移动”你的头回到所需的提交。

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
注意:(Since Git 2.7) 您也可以使用git rebase --no-autostash

git revert &lt;sha-1&gt;

“撤消”给定的提交或提交范围。 重置命令将“撤消”在给定提交中所做的任何更改。 将提交带有撤消补丁的新提交,而原始提交也将保留在历史记录中。

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

这个模式说明了哪个命令做什么。 如您所见,reset &amp;&amp; checkout 修改 HEAD

【讨论】:

您的git reset HEAD --hard &lt;commit_id&gt; 示例似乎取自***.com/questions/4114095/… - 如果是这样,您能否在署名中进行编辑?【参考方案13】:

我知道这是一个旧线程...但是由于许多人正在寻找在 Git 中撤消内容的方法,我仍然认为继续在这里提供提示可能是个好主意。

当您在 git gui 中执行“git add”或将任何内容从左上角移动到左下角时,文件的内容将存储在 blob 中,并且可以从该 blob 中恢复文件内容。

因此,即使文件未提交但必须已添加,也可以恢复该文件。

git init  
echo hello >> test.txt  
git add test.txt  

现在 blob 已创建,但它被索引引用,因此在我们重置之前它不会在 git fsck 中列出。所以我们重新设置...

git reset --hard  
git fsck  

你会得到一个悬空的 blob ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

会给你文件内容“hello”回来

为了查找未引用的提交,我在某处发现了一个提示。

gitk --all $(git log -g --pretty=format:%h)  

我将它作为 git gui 中的一个工具,它非常方便。

【讨论】:

+1。正如***.com/a/21350689/6309 中提到的,git fsck --lost-found 可以提供帮助。【参考方案14】:

我刚刚对错误的项目进行了硬重置。拯救我生命的是 Eclipse 的本地历史。据说 IntelliJ Idea 也有一个,你的编辑也可能有,值得一试:

    Eclipse help topic on Local History http://wiki.eclipse.org/FAQ_Where_is_the_workspace_local_history_stored%3F

【讨论】:

Jetbrains CLion 本地历史非常棒,为我节省了 2 个小时的工作时间 :)【参考方案15】:

制作了一个小脚本,以便更容易找到正在寻找的提交:

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \\ | egrep '^commit |Date:'

是的,它可以用 awk 或类似的东西变得更漂亮,但它很简单,我只是需要它。可能会为其他人节省 30 秒。

【讨论】:

【参考方案16】:

git reflog 并返回最后一个 HEAD 6a56624 (HEAD -> master) HEAD@0:重置:移动到 HEAD~3 1a9bf73 HEAD@1:提交:在模型中添加更改生成二进制文件

【讨论】:

【参考方案17】:

这救了我的命: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

基本上你需要运行:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob”  print $3 ’); do git cat-file -p $blob > $blob.txt; done

然后手动将文件重新组织成正确的结构。

要点:如果您没有完全 100% 了解它的工作原理,请不要使用 git reset --hard,最好不要使用它。

【讨论】:

【参考方案18】:

我的问题几乎相似。在我输入git reset --hard之前,我有未提交的文件。

谢天谢地。我设法跳过了所有这些资源。在我注意到我可以撤消 (ctrl-z) 之后。 ? 我只想将这个添加到上面的所有答案中。

注意。无法ctrl-z 未打开的文件。

How can I undo git reset --hard HEAD~1? Undo git reset --hard with uncommitted files in the staging area Recovering added file after doing git reset --hard HEAD^ 中篇文章: How To: Recover From a Git Hard Reset (Or, This is Why We Probably Shouldn’t but Totally Can Have Nice Things)

【讨论】:

没错。感谢您发布此内容,我也可以通过这种方式恢复未提交的更改。【参考方案19】:

git reset --hard - 你可以用来恢复一页,然后你可以再次从原点存储或拉出所有内容

【讨论】:

以上是关于如何撤消 git reset --hard HEAD~1?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 git reset --hard 不把我的本地文件改回原来的状态? [复制]

Git add后跟git reset删除了我的所有文件[重复]

git reset 之后的恢复

git reset --soft,--hard的区别

git reset --hard 错误:无法解析对象

git reset HEAD 与 git reset --hard HEAD的区别