首页 文章

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

提问于
浏览
6361

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

如果我做 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

30 回答

  • 8199

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

    首先是什么是HEAD?

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

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


    分离了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

    您也可以随时使用 reflog . git reflog 将显示更新 HEAD 的任何更改,并且检出所需的reflog条目会将 HEAD 设置回此提交 .

    Every time the HEAD is modified there will be a new entry in the reflog

    git reflog
    git checkout HEAD@{...}
    

    这将使您回到所需的提交


    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.
    
    • 注意:(Since Git 2.7)您也可以使用 git rebase --no-autostash .

    此架构说明了哪个命令执行的操作 . 如你所见 reset && checkout 修改 HEAD .

  • 9

    在GitKraken你可以这样做:

    • 右键单击要重置的提交,选择:重置为此提交/硬盘:

    • 再次右键单击提交,选择:当前分支名称/推送:

    • 点击强制推送:

    Obs. :您需要小心,因为硬重置后的所有提交历史都会丢失,并且此操作是不可逆转的 . 你需要确定你在做什么 .

  • 25

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

    暂时切换到其他提交

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

    # 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 manpage实际上在其描述中涵盖了很多内容 . 另一个有用的链接是this git-scm.com section discussing git-revert .

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

    在这种情况下,您可能会发现此答案很有用:
    How to move HEAD back to a previous location? (Detached head)

  • 11

    尝试重置为所需的提交 -

    git reset <COMMIT_ID>

    (检查COMMIT_ID使用 git log

    这会将所有已更改的文件重置为未添加状态 .

    现在你可以 checkout 所有未添加的文件了

    git checkout .

    检查 git log 以验证您的更改 .

    UPDATE

    如果您的仓库中有 one and only 提交,请尝试

    git update-ref -d HEAD

  • 51

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

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

    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问题,Revert to a commit by a SHA hash in Git? .

  • 1351

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

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

    这将从HEAD返回到提交哈希的所有内容,这意味着它将在工作树中重新创建该提交状态,就好像每次提交都已经被回退一样 . 然后,您可以提交当前树,它将创建一个全新的提交,基本上等同于您"reverted"的提交 .

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

    这是 safe and easy way to rollback to a previous state . 没有历史被破坏,因此它可以用于已经公开的提交 .

  • 18

    恢复到最近的提交并忽略所有本地更改:

    git reset --hard HEAD
    
  • 12

    要保持从先前提交到HEAD的更改并移至上一次提交,请执行以下操作:

    git reset <SHA>
    

    如果从先前提交到HEAD不需要更改并且只丢弃所有更改,请执行以下操作:

    git reset --hard <SHA>
    
  • 22

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

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

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

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

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

    然后它将保存您的更改 .

  • 110

    为了完全清除编码器目录中的一些意外更改,我们使用了:

    git add -A .
    git reset --hard HEAD
    

    只是 git reset --hard HEAD 将摆脱修改,但它赢得't get rid of 150843 files. In their case they' d意外地随机拖动一个重要的文件夹,所有这些文件被Git视为新的,所以 reset --hard 没有修复它 . 通过预先运行 git add -A . ,它使用git显式跟踪它们,以便通过重置消除 .

  • 48

    小心!如果用户错误地提交了错误的提交,则此命令可能导致丢失提交历史记录 . 总是有你额外的git备份,以防万一你犯错误,比你更安全一些 . :)

    我有类似的问题,并希望恢复到早期的提交 . 在我的情况下,我没有intetessered保持较新的提交,因此我使用 Hard .

    这就是我做的方式:

    git reset --hard CommitId && git clean -f
    

    这将在本地存储库上恢复,此后使用 git push -f 将更新远程存储库 .

    git push -f
    
  • 165

    假设你正在谈论master和那个分支(也就是说,这可能是你所关注的任何工作分支):

    # Revert local master branch to November 3rd commit ID
    git reset --hard 0d1d7fc32e5a947fbd92ee598033d85bfc445a50
    
    # Revert remote master branch to November 3rd commit ID
    git push -f origin 0d1d7fc32e5a947fbd92ee598033d85bfc445a50:master
    

    我在博客文章Delete remote Git repo to specific commit找到了答案 .

  • 1319

    选择所需的提交,然后进行检查

    git show HEAD
    git show HEAD~1
    git show HEAD~2
    

    直到你得到所需的提交 . 为了使HEAD指向那个,做

    git reset --hard HEAD~1
    

    git reset --hard HEAD~2 或其他什么 .

  • 31

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

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

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


    Note : 如评论中所述,don 't do this if you'与其他拥有旧提交副本的人分享您的分支

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

    git clean -i

  • 9

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

    git reset --soft HEAD~1
    
    • --soft 表示未提交的文件应保留为与 --hard 相对的工作文件,这将丢弃它们 .

    • HEAD~1 是最后一次提交 . 如果要回滚3次提交,可以使用 HEAD~3 . 如果要回滚到特定的修订版号,也可以使用其SHA哈希来执行此操作 .

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

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

  • 15

    当你的提交被远程推送时,你需要删除它们 . 让我假设您的分支正在发展并且它被推到原点 .

    首先需要从原点中删除develop:

    git push origin :develop (note the colon)
    

    然后你需要开发到你想要的状态,让我假设提交哈希是EFGHIJK:

    git reset --hard EFGHIJK
    

    最后,推动再次发展:

    git push origin develop
    
  • 101

    Revert是回滚提交的命令 .

    git revert <commit1> <commit2>
    

    样品:

    git revert 2h3h23233

    它能够从HEAD中获取范围,如下所示 . 这里1表示“还原最后一次提交” .

    git revert HEAD~1..HEAD

    然后做 git push

  • 54

    完成所有更改后,当您推送所有这些命令时,您可能必须使用:

    git push -f ...
    

    而且不仅 git push .

  • 45

    有一个命令(不是核心Git的一部分,但它在git-extras包中)专门用于恢复和暂存旧提交:

    git back
    

    根据man page,它也可以这样使用:

    # Remove the latest three commits
    git back 3
    
  • 123

    OK, 回到以前的git提交很容易......

    还原 without keeping 更改:

    git reset --hard <commit>
    

    还原 with keeping 更改:

    git reset --soft <commit>
    

    Explain: 使用git reset,你可以重置为一个特定的状态,如上所述,它通常使用提交哈希 .

    但正如您所看到的区别在于使用两个标志 --soft--hard ,默认 git reset 使用 --soft 标志,但这是一个很好的做法总是使用标志,我解释每个标志:


    --soft

    解释的默认标志,不需要提供它,不会更改工作树,但添加准备提交的所有更改文件,因此您将返回到提交状态,更改为文件将被取消暂停 .


    - 硬

    小心这个标志,它会重置工作树以及对跟踪文件的所有更改,一切都将消失!


    我还创建了下面的图像,可能发生在使用git的现实生活中:

  • 87

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

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

    创建一个Bash 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> 超出我的范围 .

  • 49

    我相信有些人可能会想要知道如何回滚他们在他们的主人身上做出的改变 - 即抛弃一切并返回原点/主人,在这种情况下,执行此操作:

    git reset --hard origin/master
    

    https://superuser.com/questions/273172/how-to-reset-master-to-origin-master

  • 44

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

    git add . && git checkout master -f
    

    简短的介绍:

    • 它不会像 git revert 那样创建任何提交 .

    • 它不会像 git checkout <commithashcode> 那样分离你的头部 .

    • 它将覆盖所有本地更改并删除自分支中最后一次提交以来所有添加的文件 .

    • 它仅适用于分支名称,因此您只能以这种方式恢复分支中的最新提交 .

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

    git add . && git reset --hard HEAD
    

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

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

  • 33

    这是回到之前提交的一种 much simpler 方式(让它处于非公开状态,无论你喜欢什么,都可以使用它):

    git reset HEAD~1
    

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

  • 22

    如果要在上次提交中更正某些错误,则可以使用 git commit --amend 命令 . 如果任何引用都没有指向最后一次提交,那么这将起到作用,因为它创建一个与最后一次提交具有相同父级的提交 . 如果没有对最后一次提交的引用,它将被简单地丢弃,并且此提交将是最后一次提交 . 这是在不恢复提交的情况下纠正提交的好方法 . 但它有其自身的局限性 .

  • 19

    您可以自己完成所有这些初始步骤,然后再回到git repo .

    • 使用 git pull --all 命令从Bitbucket中提取最新版本的存储库 .

    • 从终端运行带-n 4的git log命令 . -n之后的数字确定从本地历史记录中最近一次提交开始的日志中的提交数 .

    $ git log -n 4

    • 使用 git reset --hard HEAD~N 重置存储库历史记录的头部,其中N是您想要返回的提交数 . 在以下示例中,head将被设置为一次提交,以及存储库历史记录中的最后一次提交:

    • 使用 git push --force 将更改推送到git repo以强制推送更改 .

    如果你想要git存储库到以前的提交

    git pull --all
    git reset --hard HEAD~1
    git push --force
    
  • 19

    如果情况是 urgent one ,并且您只想以 quick and dirty 方式执行提问者所要求的操作,假设您的项目位于目录"my project"下:

    • 复制整个目录并将其称为其他内容,例如“我的项目 - 复制”

    • 做:

    git reset --hard [first-4-letters&numbers-of-commit's-SHA]
    

    然后,您的系统上有两个版本...您可以检查或复制或修改先前提交中感兴趣的文件或其他任何内容 . 你可以完全放弃“我的项目 - 复制”下的文件,如果你已经决定新的工作无处可去......

    很明显,如果你想继续执行项目状态而不实际丢弃工作,因为这个检索到的提交是重新命名你的目录:删除包含检索到的提交的项目(或给它一个临时名称)并重命名你的“我的项目 - 将“目录复制回”我的项目“ . 然后可能很快就会做另一次提交 .

    Git是一个出色的创作,但你不能只是"pick it up on the fly":也试图解释它的人 far too often 假设其他VCS [版本控制系统]的先前知识并且太快深入钻研,并犯下其他罪行,例如使用可互换的术语对于"checking out" - 有时看起来几乎可以混淆初学者的方式 .

    为了节省很多压力,你必须要读一本关于Git的书 - 我推荐"Version Control with Git" . 如果你说"have to"时你可以信任我(或者更确切地说是我的伤疤),那么你可以这样做 NOW . Git的大部分复杂性来自分支然后重新合并 . 但是根据你的问题,有 no reason why people should be blinding you with science .

    特别是如果,例如,这是一个绝望的情况,你是Git的新手!

    PS:另一个想法:它(现在)实际上是相当的将Git存储库("repo")保存在除具有工作文件的目录之外的目录中 . 这意味着你不必使用上面的快速和脏的解决方案来复制整个Git存储库 . 请参阅Fryer使用--separate-git-dir here的答案 . Be warned ,但是:如果你有一个"separate-directory"存储库而你没有复制,并且你进行了硬复位,那么重置提交之后的所有版本将永远丢失,除非你绝对应该定期备份你的存储库,最好到 Cloud (例如Google Drive)等地方 .

  • 19

    Jefromi解决方案的额外替代品

    Jefromi's solutions绝对是最好的,你绝对应该使用它们 . 但是,为了完整起见,我还想展示这些其他替代解决方案,这些解决方案也可用于恢复提交(在某种意义上,你 create a new commit that undoes changes in previous commit ,就像 git revert 所做的那样) .

    要清楚,这些替代方案 are not the best way to revert commitsJefromi's solutions are,但我只是想指出,您也可以使用这些其他方法来实现与 git revert 相同的功能 .

    备选1:硬重置和软重置

    这是Charles Bailey对Revert to a commit by a SHA hash in Git?的解决方案的略微修改版本:

    # 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的Checkout old commit and make it a new commit解决方案:

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

    与备选#1类似,它会在当前工作副本中重现 <commit> 的状态 . 首先必须执行 git rm ,因为 git checkout 不会删除自 <commit> 以来添加的文件 .

  • 10

    除了这个确切的组合,这里没有任何东西对我有用:

    git reset --hard <commit_hash>
    git push origin <branch_name> --force
    

    这里的关键是强制推送,没有额外的提交/提交消息等 .

  • 9

    这是直接重置为最近提交的另一种方法

    git stash
    git stash clear
    

    它直接清除自上次提交以来您所做的所有更改 .

    PS:它有一点问题;它还会删除您最近存储的所有存储更改 . 在大多数情况下,我猜这应该不重要 .

相关问题