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


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

git reset --hard HEAD~1

914
2017-08-07 23:22


起源


我已经写了一个完整的指南,用git恢复任何丢失的提交。它甚至有插图:-) [查看] [fixLink] [fixLink]: programblings.com/2008/06/07/... - webmat
--hard 丢弃未提交的更改。由于这些没有被git跟踪,因此无法通过git恢复它们。 - Zaz
stackoverflow.com/questions/34519665/... - CodeWizard
这个 是一篇很棒的文章,可以帮助您恢复文件。 - worker11811
这是Github的一个很好的资源: 如何用Git撤消(几乎)任何东西 - jasonleonhard


答案:


Pat Notz是对的。只要在几天之内,您就可以获得提交。除非你明确告诉它删除更新的blob,否则git只会在大约一个月后收集垃圾。

$ 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重置时,它被放回原位。


1402
2017-08-22 04:36



你可以使用“git reset --hard HEAD @ {1}”,不需要使用SHA1。在大多数情况下,使用“git reset --hard ORIG_HEAD”就足够了。 - Jakub Narębski
git log -g 查看reflog比查看更好的方式 git reflog。 - Dan Moulding
这有一个非常重要的警告......那就是“ - 硬”部分。 --hard会吹走你当地未提交的更改。你不能让他们这样回来(因为他们没有在任何地方承诺)。我相信你无能为力:( - Michael Anderson
^只是因为你知道你可以在重置之前存储你的本地更改 - 硬件,然后只是弹出它们而你什么都不会丢失!得爱git。 - Lethjakman
我更喜欢 git reflog 过度 git log -g 只是因为你在一行上获得了sha1,HEAD信息和提交消息的所有信息。更容易阅读。 - Snowcrash


您要做的是指定要还原到的提交的sha1。你可以通过检查reflog来获得sha1(git reflog)然后做

git reset --hard <sha1 of desired commit>

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


313
2017-08-09 05:30



警告 对于我自己几分钟前:这将重置所有修改。但它不会触及未跟踪的文件。 - aexl


答案隐藏在上面的详细回复中,您可以简单地做到:

$> git reset --hard HEAD@{1}

(见输出 git reflog show


123
2018-02-26 15:13



为什么这不是公认的答案? - sarink
这完全没问题。谢谢。
请注意,如果您在重置后进行了任何其他repo更改,则这不是解决方案。在运行任何东西之前一定要看一下reflog。 - forresthopkinsa
意外重置我的存储库,认为我的工作永远丢失了。这个答案救了我的一天。 - Zaiyang Li


如果Git还没有收集垃圾,就可以恢复它。

获取有关悬挂提交的概述 fsck

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

使用rebase恢复悬空提交:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

82
2017-08-22 06:11



你保存了我最近6天的工作。谢谢。 - Shubham Abrol
这太棒了,谢谢! - mrtaz
这真是太棒了。生活得救了。 - Mohhamad Hasham


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

我知道这不是一个正确的答案,但它为我节省了半天的工作,所以希望它能为其他人做同样的事情!


33
2018-01-13 00:48



这实际上是一个非常好的提示,为我节省了很多次;)它的方式比在git中做任何事情都简单... - severin
并感谢你们在这整个世界中所有的善良。谢谢。谢谢。谢谢。 - Trip
这是在硬重置后恢复文件中未分级更改的唯一方法。也救了我;) - Czarek Tomczak
作为一个额外的提示,一些IDE作为eclipse也保存了最近的文件历史记录。这样,您甚至可以在编辑器关闭后恢复较旧的更改。这给我带来了奇迹。 - martin
上帝保佑你克里斯 - Adam Waite


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.

27
2017-08-15 07:01



晃来晃去的blob 听起来像一个AD&D怪物! - sbichenko
@Stian Thx男人,你救了我的一天 - Adel Bachene
@Stian git show ==屁股保存了ftw - Stan
谢谢@Stian好解释!我想为其他人找到这个答案,如果你有多个“悬空”提交它不确定你想在最后一行做rebase :) - JimiSweden
谢谢你救了我@Stian - Tomasz


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

$ git reflog

将列出:

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

哪里 4bac331 是个 discarded commit

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

$ git reset --hard 4bac331

25
2018-04-02 08:57





在大多数情况下,是的。

根据运行命令时存储库所处的状态,具有的效果 git reset --hard 可以从琐碎到撤消,到基本上不可能。

下面我列出了一系列不同的可能方案,以及如何从中恢复。

我的所有更改都已提交,但现在提交已经完成!

运行时通常会发生这种情况 git reset 与争论,如在 git reset --hard HEAD~。别担心,这很容易恢复!

如果你刚跑了 git reset 并且没有做任何其他事情,因为你可以回到这个单线的位置:

git reset --hard @{1}

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

但是,如果你  自复位后对您的分支进行了其他修改,上面的单行无效。相反,你应该跑 git reflog  <branchname> 查看最近对您的分支所做的所有更改的列表(包括重置)。该列表看起来像这样:

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~”。然后复制提交的表示 之前 (下面)那个操作。在我们的例子中,那就是 master@{1}(要么 3ae5027,它们都代表相同的提交),然后运行 git reset --hard <commit> 将当前分支重置回该提交。

我上演了我的改动 git add,但从未犯下过。现在我的变化消失了!

恢复起来有点棘手。混帐  拥有您添加的文件的副本,但由于这些副本从未与任何特定提交相关联,因此您无法一次性恢复所有更改。相反,您必须在git的数据库中找到各个文件并手动恢复它们。你可以这样做 git fsck

有关详细信息,请参阅 使用暂存区域中的未提交文件撤消git reset --hard

我对工作目录中的文件进行了更改,这是我从未上过的 git add,从未承诺过。现在我的变化消失了!

哦,哦。我不想告诉你这个,但你可能运气不好。 git不存储您不添加或提交的更改,并根据 的文件 git reset

- 硬

重置索引和工作树。 自那以后对工作树中跟踪文件的任何更改 <commit> 被丢弃了。

你有可能 威力 能够使用某种磁盘恢复工具或专业数据恢复服务恢复您的更改,但此时可能比它的价值更麻烦。


23
2018-01-16 18:37



One-liner为我工作,谢谢,但我只是想知道“@ {1}”究竟是什么。 - Stan Bashtavenko
@StanB文档在这里: git-scm.com/docs/git-rev-parse 基本上它指的是当前分支上的第一个reflog条目。 - Ajedi32
感谢您覆盖所有案例。我没有承诺或加入我的。 - xdhmoore


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

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

较新版本的Git有一些称为“reflog”的东西,它是对refs所做的所有更改的日志(与对存储库内容所做的更改相反)。因此,例如,每次切换HEAD时(即每次进行操作时) git checkout 切换分支)将被记录。而且,当然,你的 git reset 还操纵了HEAD,因此它也被记录了。您可以使用类似的方式访问refs的旧状态,以便访问存储库的旧状态 @ 标志而不是 ~, 喜欢 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指向的提交”。

这将很容易让您找到丢失的提交并恢复它。


20
2017-08-27 03:45



我认为另一种解释会更清楚:HEAD~1意味着去“HEAD的父母”,而HEAD @ {1}则“回到HEAD历史中的一步” - kizzx2
问题是术语“历史”在VCS中确实过载了。另一种表达方式是〜倒退 提交历史,而@倒退了 时间顺序或时间历史。但这三个版本中没有一个特别好。 - Jörg W Mittag
@ kizzx2(和Jorg)实际上这3个解释,当合在一起时,帮助很多 - thx - Richard Le Mesurier


我知道这是一个老线程......但是由于许多人正在寻找在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的工具,它非常方便。


10
2018-01-20 06:27



+1。如上所述 stackoverflow.com/a/21350689/6309, git fsck --lost-found 可以帮助。 - VonC


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

First of all what is HEAD?

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

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


detached HEAD

如果你不是最近的提交 - 意思是 HEAD 指向历史上的先前提交它被称为 detached HEAD

enter image description here

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

enter image description here


关于如何从分离的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

你可以随时使用 reflog 同样。
git reflog 将显示更新的任何更改 HEAD 并检查所需的reflog条目将设置 HEAD 回到这个提交。

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

git reflog
git checkout HEAD@{...}

这将使您回到所需的提交

enter image description here


git reset HEAD --hard <commit_id>

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

# 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.
  • 注意: (自Git 2.7起
    你也可以使用 git rebase --no-autostash 同样。


git revert <sha-1>

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

# 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 && checkout 修改 HEAD

enter image description here


4
2018-04-21 16:30



看来你的 git reset HEAD --hard <commit_id> 例子取自 stackoverflow.com/questions/4114095/...  - 如果是这样的话,你可以编辑归属地吗? - Rob♦