是否可以撤消由以下命令引起的更改?如果是,怎么办?
git reset --hard HEAD~1
是否可以撤消由以下命令引起的更改?如果是,怎么办?
git reset --hard HEAD~1
当前回答
答案隐藏在上面的详细响应中,您可以简单地执行以下操作:
$> git reset --hard HEAD@{1}
(参见git-relog show的输出)
其他回答
在回答之前,让我们添加一些背景,解释一下这个HEAD是什么。
首先,什么是头部?
HEAD只是对当前分支上当前提交(最新)的引用。在任何给定时间只能有一个HEAD。(不包括git工作树)
HEAD的内容存储在.git/HEAD中,它包含当前提交的40字节SHA-1。
分离式封头
如果您不在最近一次提交中,这意味着HEAD指向历史上的先前提交,它称为分离的HEAD。
在命令行上,它看起来像-SHA-1而不是分支名称,因为HEAD没有指向当前分支的尖端
关于如何从分离的HEAD恢复的几个选项:
git校验
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>
数字刷新
您也可以始终使用reflog。git reflog将显示更新HEAD的任何更改,检查所需的reflog条目将将HEAD设置回该提交。
每次修改HEAD时,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.
注:(自Git 2.7以来)您也可以使用git rebase--也可以不使用autostash。
git还原<sha-1>
“撤消”给定的提交或提交范围。重置命令将“撤消”给定提交中所做的任何更改。将提交带有撤销补丁的新提交,而原始提交也将保留在历史记录中。
# 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>
此模式说明了哪个命令执行什么操作。正如您可以看到的那样,重置和签出修改HEAD。
您要做的是指定要还原到的提交的sha1
git reset --hard <sha1 of desired commit>
但不要等太久。。。几周后,git将最终看到提交未被引用,并删除所有blob。
如果您使用的是JetBrains IDE(任何基于IntelliJ的),您甚至可以通过其“本地历史记录”功能恢复未提交的更改。
右键单击文件树中的顶级目录,在上下文菜单中找到“本地历史记录”,然后选择“显示历史记录”。这将打开一个视图,在该视图中可以找到您最近所做的编辑,一旦找到要返回的修订,请右键单击该修订并单击“还原”。
如果您还没有对存储库进行垃圾收集(例如,使用git重新打包-d或git gc,但请注意垃圾收集也可以自动进行),那么您的提交仍然存在,只是无法通过HEAD访问。
您可以尝试通过查看git fsck的输出来查找提交。
较新版本的Git有一种叫做“reflog”的东西,它是对ref所做的所有更改的日志(与对存储库内容所做的更改相反)。因此,例如,每次您切换HEAD时(即每次执行git签出以切换分支时),都会被记录。当然,你的git重置也操纵了HEAD,所以它也被记录了下来。您可以以类似于访问存储库的旧状态的方式访问ref的旧状态,方法是使用@符号而不是~,如git-resetHEAD@{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指向的提交”。
这将很容易让您找到丢失的提交并恢复它。
在大多数情况下,是的。
根据运行命令时存储库的状态,git reset-hard的效果可能从微不足道到撤消,甚至根本不可能。
下面我列出了一系列不同的可能情况,以及如何从中恢复。
我的所有更改都已提交,但现在提交都已取消!
这种情况通常发生在使用参数运行git-reset时,如git-reset--hardHEAD~。别担心,这很容易恢复!
如果您刚刚运行了git reset,并且此后没有做任何其他操作,那么您可以回到使用这一行程序的位置:
git reset --hard @{1}
这将重置当前分支,无论它在上次修改之前处于什么状态(在您的情况下,对分支的最近修改将是您尝试撤消的硬重置)。
但是,如果在重置之后对分支进行了其他修改,则上面的一行代码将不起作用。相反,您应该运行gitreflog<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>将当前分支重置为该提交。
我用gitadd进行了更改,但从未提交。现在,我的改变消失了!
这有点难以恢复。git确实有您添加的文件的副本,但由于这些副本从未绑定到任何特定的提交,因此无法一次恢复所有更改。相反,您必须在git的数据库中找到各个文件并手动恢复它们。您可以使用git fsck执行此操作。
有关此操作的详细信息,请参阅Undo git reset-hard with subcommitted files in the staging area。
我对工作目录中的文件进行了更改,这些更改从未使用gitadd进行过,也从未提交过。现在,我的改变消失了!
噢。我不想告诉你这些,但你可能运气不好。git不存储未添加或提交的更改,根据git reset文档:
--硬的,硬的重置索引和工作树。自<commit>以来,对工作树中跟踪文件的任何更改都将被丢弃。
您可能可以使用某种磁盘恢复实用程序或专业的数据恢复服务来恢复更改,但在这一点上,这可能会带来更多的麻烦。