题 如何将Git存储库还原为先前的提交


如何从当前状态恢复为在某个提交时创建的快照?

如果我做 git log,然后我得到以下输出:

$ git log
commit a867b4af366350be2e7c21b8de9cc6504678a61b`
Author: Me <me@me.com>
Date:   Thu Nov 4 18:59:41 2010 -0400

blah blah blah...

commit 25eee4caef46ae64aa08e8ab3f988bc917ee1ce4
Author: Me <me@me.com>
Date:   Thu Nov 4 05:13:39 2010 -0400

more blah blah blah...

commit 0766c053c0ea2035e90f504928f8df3c9363b8bd
Author: Me <me@me.com>
Date:   Thu Nov 4 00:55:06 2010 -0400

And yet more blah blah...

commit 0d1d7fc32e5a947fbd92ee598033d85bfc445a50
Author: Me <me@me.com>
Date:   Wed Nov 3 23:56:08 2010 -0400

Yep, more blah blah.

如何从11月3日恢复到提交,即提交 0d1d7fc


6021
2017-11-06 16:58


起源


有关 如何撤消上次Git提交?。
这里的 一个非常明确和彻底的帖子 关于在Git中撤消事物,直接来自Github。 - Nobita
有关: 回滚到公共仓库中的旧Git提交。请注意,该问题会添加一个约束,即repo是公共的。
我喜欢git,但事实上,对于一些应该非常简单的事情有35个答案会暴露出一个巨大的问题。还是文档? - The Muffin Man


答案:


这很大程度上取决于“恢复”的含义。

暂时切换到其他提交

如果你想暂时回到它,傻瓜,然后回到你所在的位置,你所要做的就是检查所需的提交:

# This will detach your HEAD, that is, leave you with no branch checked out:
git checkout 0d1d7fc32

或者,如果你想在那里做出提交,那么在你做的时候继续做一个新的分支:

git checkout -b old-state 0d1d7fc32

要回到原来的位置,只需查看您再次访问的分支。 (如果你做了更改,就像转换分支时一样,你必须在适当的时候处理它们。你可以重置它们扔掉它们;你可以藏匿,结账,存放pop以带走它们;你可以提交如果你想要那里的分支,他们到那里的一个分支。)

硬删除未发布的提交

另一方面,如果你想真正摆脱自那时以来所做的一切,那么有两种可能性。一,如果您还没有发布任何这些提交,只需重置:

# 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,revert有一个非常具体的含义:使用反向补丁创建一个提交以取消它。这样您就不会重写任何历史记录。

# This will create three separate revert commits:
git revert a867b4af 25eee4ca 0766c053

# It also takes ranges. This will revert the last two commits:
git revert HEAD~2..HEAD

#Similarly, you can revert a range of commits using commit hashes:
git revert a867b4af..0766c053 

# Reverting a merge commit
git revert -m 1 <merge_commit_sha>

# To get just one, you could use `rebase -i` to squash them afterwards
# Or, you could do it manually (be sure to do this at top level of the repo)
# get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .

# Then commit. Be sure and write a good message describing what you just did
git commit

git-revert 手册页 实际上在其描述中涵盖了很多内容。另一个有用的链接是 这个git-scm.com节讨论了git-revert

如果您决定不想还原,则可以还原还原(如此处所述)或重置为还原之前(请参阅上一节)。

在这种情况下,您可能会发现此答案很有用:
如何将HEAD移回以前的位置? (独立头)


7841
2017-11-06 17:04



@罗德的评论 上 git revert HEAD~3 作为最好的回归 3 承诺是重要的惯例。 - New Alexandria
你能写出整数吗?喜欢: git reset --hard 0d1d7fc32e5a947fbd92ee598033d85bfc445a50 - Spoeken
@MathiasMadsenStav是的,您当然可以通过完整的SHA1指定提交。我使用缩写哈希来使答案更具可读性,如果你打字,你也倾向于使用它们。如果您正在复制和粘贴,请务必使用完整哈希。看到 在man git rev-parse中指定修订版 有关如何命名提交的完整说明。 - Cascabel
要恢复当前命令是'git checkout master' - Xavier John
您可以使用 git revert --no-commit hash1 hash2 ... 在此之后,只需在一次提交中提交每个恢复 git commit -m "Message" - Mirko Akov


将工作副本还原为最近的提交

要恢复到先前的提交,请忽略任何更改:

git reset --hard HEAD

其中HEAD是当前分支中的最后一次提交

将工作副本还原为较旧的提交

要恢复到比最近提交更早的提交:

# Resets index to former commit; replace '56e05fced' with your commit code
git reset 56e05fced 

# Moves pointer back to previous HEAD
git reset --soft HEAD@{1}

git commit -m "Revert to 56e05fced"

# Updates working copy to reflect the new commit
git reset --hard

积分转到类似的Stack Overflow问题, 在Git中恢复由SHA哈希提交?


1262
2017-08-21 06:19



我做到了,但后来我无法提交并推送到远程存储库。我想要一个特定的旧提交成为HEAD ...... - Lennon
这意味着你已经推进了你想要恢复的提交。对于已签出代码并进行操作的人来说,这可能会带来很多问题。因为他们不能顺利地对你的提交进行申请。在这种情况下,更好的做一个git还原。如果您是唯一使用回购的人。做一个git push -f(但在做之前要三思而后行) - vinothkr
强制警告:不要硬复位 如果你与拥有旧提交副本的其他人共享你的分支,因为使用这样的硬重置将迫使他们必须重新同步他们的工作与新重置的分支。软重置是安全的,以及最后的解决方案 这个答案。
我还想指出,或者对于软复位解决方案,不是先进行混合复位,最后是硬复位,实际上可以先进行硬复位,如下所示: git reset --hard 56e05fc; git reset --soft HEAD@{1}; git commit。
@nuton linus自称是git的创造者,批评它过于复杂。他有记录表示他感到“震惊”,因为它的复杂性使git变得如此受欢迎 - boulder_ruby


这里有很多复杂而危险的答案,但实际上很简单:

git revert --no-commit 0766c053..HEAD
git commit

这将从HEAD返回到提交哈希的所有内容,这意味着它将在工作树中重新创建该提交状态 仿佛 从那以后的每一次承诺都被退了回来。然后,您可以提交当前树,它将创建一个全新的提交,基本上等同于您“恢复”的提交。

(该 --no-commit flag允许git立即恢复所有提交 - 否则,系统会提示您为该范围内的每个提交发送一条消息,并使用不必要的新提交乱丢您的历史记录。)

这是一个 回滚到以前状态的安全简便方法。没有历史被破坏,因此它可以用于已经公开的提交。


1221
2018-02-12 04:18



如果你确实想要进行单独提交(而不是通过一次大提交来恢复所有提交),那么你可以通过 --no-edit 代替 --no-commit,这样您就不必为每次还原编辑提交消息。
如果0766c053..HEAD之间的一个提交是合并,那么将弹出一个错误(与没有指定-m一样)。这可能有助于那些遇到: stackoverflow.com/questions/5970889/... - timhc22
在提交使用之前查看差异 git diff --cached。 - John Erck
$ git revert --no-commit 53742ae..HEAD 回报 fatal: empty commit set passed - Alex G
@AlexG那是因为你需要输入哈希值 一个之前 你要回去的那个。就我而言,哈希就像: 81bcc9e HEAD{0}; e475924 HEAD{1}, ...(从 git reflog),我想撤消我所做的事情 81bcc9e,然后我不得不这样做 git revert e475924..HEAD - EpicPandaForce


我和其他人的最佳选择是Git重置选项:

git reset --hard <commidId> && git clean -f

这对我来说是最好的选择!它简单,快速,有效!


注意 :  如评论中所述,如果您与拥有旧提交副本的其他人共享您的分支,则不会这样做

同样来自评论,如果你想要一个较少'ballzy'的方法,你可以使用

git clean -i 


153
2017-10-22 11:53



强制性警告:不要这样做 如果你与拥有旧提交副本的其他人共享你的分支,因为使用这样的硬重置将迫使他们必须重新同步他们的工作与新重置的分支。 对于解决方案,详细解释如何安全地还原提交而不会丢失硬重置工作, 看到这个答案。
我第二次@ Cupcake的警告......非常清楚后果。但请注意,如果你真的需要让这些提交永远从历史中消失,那么这个reset + clean方法就可以实现,你需要 力 将修改后的分支推回到任何和所有遥控器。 - ashnazg
请记住,clean将删除您的设置/ IDE可能使用的文件/文件夹,例如.idea(phpstorm)或.vagrant(vagrant)! - timhc22
git clean -f DANGER DANGER - Tisch
@Pogrindis - 这里有很多很好的答案,不删除未跟踪的文件。 - Tisch


如果要“取消提交”,擦除最后一次提交消息,并将修改后的文件放回到分段中,则可以使用以下命令:

git reset --soft HEAD~1
  • --soft 表示未提交的文件应保留为与之对应的工作文件 --hard 哪会丢弃它们。
  • HEAD~1 是最后一次提交。如果你想回滚3个提交,你可以使用 HEAD~3。如果要回滚到特定的修订版号,也可以使用其SHA哈希来执行此操作。

在您提交了错误的内容并且想要撤消上次提交的情况下,这是一个非常有用的命令。

资源: http://nakkaya.com/2009/09/24/git-delete-last-commit/


103
2018-03-04 17:25



柔软轻柔:如果你没有推动你的工作,就没有风险 - nilsM


在回答之前,让我们添加一些背景,解释一下这个 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 同样。

此架构说明了哪个命令执行的操作。正如你在那里看到的那样 reset && checkout 修改 HEAD

Enter image description here


102
2018-02-05 21:56



优秀的暗示 git reflog,这正是我所需要的 - smac89
哎哟!这一切看起来都非常复杂......是不是只有一个简单的命令让你退后一步呢?就像从项目中的1.1版回到1.0版一样?我期待类似的东西:git stepback_one_commit或者其他什么...... - Kokodoko
有: git reset HEAD^ --hard` - CodeWizard
@Kokodoko是的,它非常复杂......并且是一个很好的例子,说明专家对刚刚开始的人很少考虑。请参考我的答案,以及我推荐的书。 Git不是你可以直观地获取的东西。我绝对可以肯定CodeWizard没有这样做。 - mike rodent


我已经尝试了很多方法来恢复Git中的本地更改,如果你只想恢复到最新的提交状态,这似乎是最好的。

git add . && git checkout master -f

简短的介绍:

  • 它不会创建任何提交 git revert 确实。
  • 它不会像你那样分离你的头部 git checkout <commithashcode> 确实。
  • 它将覆盖所有本地更改并删除自分支中最后一次提交以来所有添加的文件。
  • 它仅适用于分支名称,因此您只能以这种方式恢复分支中的最新提交。

我找到了一种更方便,更简单的方法来实现上述结果:

git add . && git reset --hard HEAD

其中HEAD指向您当前分支的最新提交。

它与boulder_ruby建议的代码相同,但我已添加 git add . 之前 git reset --hard HEAD 擦除自上次提交以来创建的所有新文件,因为这是大多数人在恢复到最新提交时所期望的。


96
2017-07-29 11:01





您可以通过以下两个命令执行此操作:

git reset --hard [previous Commit SHA id here]
git push origin [branch Name] -f

它将删除您之前的Git提交。

如果您想保留更改,还可以使用:

git reset --soft [previous Commit SHA id here]

然后它将保存您的更改。


76
2017-12-12 06:52



我在这篇文章中尝试了几十个答案,直到我得到了这个...所有其他人,我的git配置在尝试推送时一直给我一个错误。这个答案有效。谢谢! - gnB
对我来说,一个细节就是我失去了差异......我想继续看看我在提交中做了什么不起作用。所以下次我会在发出这个reset命令之前保存那些数据 - gnB
这是我撤消错误合并的唯一方法,在这种情况下恢复不起作用。谢谢! - Dave Cole


假设您在名为的文本文件中有以下提交 ~/commits-to-revert.txt (我用了 git log --pretty=oneline 得到他们)

fe60adeba6436ed8f4cc5f5c0b20df7ac9d93219
0c27ecfdab3cbb08a448659aa61764ad80533a1b
f85007f35a23a7f29fa14b3b47c8b2ef3803d542
e9ec660ba9c06317888f901e3a5ad833d4963283
6a80768d44ccc2107ce410c4e28c7147b382cd8f
9cf6c21f5adfac3732c76c1194bbe6a330fb83e3
fff2336bf8690fbfb2b4890a96549dc58bf548a5
1f7082f3f52880cb49bc37c40531fc478823b4f5
e9b317d36a9d1db88bd34831a32de327244df36a
f6ea0e7208cf22fba17952fb162a01afb26de806
137a681351037a2204f088a8d8f0db6e1f9179ca

创建一个 巴什 用于还原每个脚本的shell脚本:

#!/bin/bash
cd /path/to/working/copy
for i in `cat ~/commits-to-revert.txt`
do
    git revert $i --no-commit
done

这会将所有内容还原回以前的状态,包括文件和目录创建以及删除,将其提交到您的分支并保留历史记录,但是您将其还原为相同的文件结构。为什么Git没有 git revert --to <hash> 超出我的范围。


51
2017-10-13 21:51



你可以做一个 git revert HEAD~3 删除最后3个提交 - Rod
@Rod - 不,那不对。该命令将恢复作为HEAD的第三个祖父母(不是最后三个提交)的提交。 - kflorence
@Rod - 听起来不错,肯定是一种难看的语法,不是吗?我总是找到检查我想要“恢复”的提交,然后提交更直观的提交。 - kflorence
@Lance我同意你关于git revert的看法 - 这似乎是一种难以置信的复杂方式来实现一个非常简单的事情! - Mike Vella
有一个 更容易 现在这样做的方式比使用这样的脚本,只需使用 git revert --no-commit <start>..<end>因为 git revert 接受新的(或所有?)版本的Git中的提交范围。请注意,范围的开头不包含在恢复中。


Jefromi解决方案的额外替代品

Jefromi的解决方案 绝对是最好的,你一定要使用它们。但是,为了完整起见,我还想展示这些其他替代解决方案,这些解决方案也可以用来恢复提交(从某种意义上说,你) 创建一个新的提交,撤消先前提交中的更改,就像什么 git revert 一样)。

要明确这些替代方案 不是恢复提交的最佳方式Jefromi的解决方案是,但我只是想指出,你也可以使用这些其他方法来实现同样的目的 git revert

备选方案1:硬重置和软重置

这是Charles Bailey解决方案的略微修改版本 在Git中恢复由SHA哈希提交?

# Reset the index to the desired commit
git reset --hard <commit>

# Move the branch pointer back to the previous HEAD
git reset --soft HEAD@{1}

# Commit the changes
git commit -m "Revert to <commit>"

这基本上可以通过使用软重置将使先前提交的状态保留在索引/暂存区域中的事实来实现,然后您可以提交该区域。

备选方案2:删除当前树并替换为新树

这个解决方案来自svick的解决方案 签出旧提交并使其成为新提交

git rm -r .
git checkout <commit> .
git commit

与替代#1类似,这再现了状态 <commit> 在当前的工作副本中。有必要这样做 git rm 首先因为 git checkout 不会删除自那以后添加的文件 <commit>


48
2018-02-29 08:40



关于备选方案1,一个简单的问题:通过这样做,我们不会在提交之间松动吗? - Bogac
在备选方案2中,圆点代表那些命令中的内容? - Bogac
@Bogac - 点表示文件路径,在本例中是当前目录,因此假设您从工作副本的根目录运行它。 - Tom


这里有一个 更简单 返回到先前提交的方式(并使其处于未通信状态,无论你喜欢什么,都可以使用它):

git reset HEAD~1

所以,不需要提交ID等等:)


45
2018-05-10 17:21



没有工作,这之后的git pull产生:错误:您对以下文件的本地更改将被合并覆盖: - malhal
@malhal那是因为你有一些未经修改的变化。存储/重置它们然后它将工作没有该错误。 - Paul Walczewski