如何在Git中恢复丢失的提交?

516

首先,收到了“您的分支领先于origin/master 3个提交”的消息,然后我的应用程序回滚到了早期的时间并且有早期的更改。

我该如何找回过去11个小时所做的工作?


你是否已经查看了以下链接:https://dev59.com/7HVC5IYBdhLWcg3wfhKL、https://dev59.com/SnE95IYBdhLWcg3wKqok和https://dev59.com/_XE95IYBdhLWcg3wXcrd? - Mikel
我发现这个链接非常有帮助:https://www.geeksforgeeks.org/recovering-lost-commits-in-git/ 基本上,TL;DR:切换到你想要提交的分支,运行 git reflog,找到你的提交,然后运行 git cherry-pick <commit-hash> - undefined
9个回答

1132

git reflog是你的好朋友。在列表中找到你想要回到的提交记录,然后可以重置到该提交记录(例如:git reset --hard e870e41)。

(如果你还没有提交你的更改……那可能有麻烦了——尽早提交,频繁提交!)


24
看一下 git log HEAD@{1} 的记录。如果它看起来像是正确的提交序列,那么可以运行 git reset HEAD@{1} - Amber
17
只有当代码被暂存(使用 git add)后,它们才会保存在 git 中,并且可以轻松地使用命令(例如 git fsck --lost-found)找回。 - Landys
10
在变基时,我不小心删除了一个本应该保留的提交。这却让我省去了重新做几个小时工作的麻烦。 - josephting
90
这个救了我的命。 - Lutaaya Huzaifah Idris
4
git-gc会最终清除旧的reflog条目,一旦没有任何引用提交,它的对象也将最终被清除。可以通过git-gc的配置选项设置清理reflog的条件https://git-scm.com/docs/git-gc#Documentation/git-gc.txt-gcreflogExpire(在本评论撰写时,默认到期时间为可达提交的reflog条目90天,不可达当前提示的提交则为30天)。 - Amber
显示剩余6条评论

198
在回答之前,让我们先了解一些背景知识,解释一下什么是 HEAD

首先,什么是 HEAD?

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

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


脱离 HEAD

如果您没有处于最新提交状态 - 意味着HEAD指向历史记录中的先前提交,则称为脱离 HEAD

Enter image description here

在命令行中,它会显示为这样 - SHA-1代替分支名称,因为HEAD没有指向当前分支的末尾:

Enter image description here

Enter image description here


从游离状态恢复的几种选项:


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 --hard <commit_id>

将您的HEAD退回至所需提交记录。

# 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>

撤销给定的提交或提交范围。
重置命令将“撤消”在给定提交中进行的任何更改。
一个包含撤消补丁的新提交将被提交,而原始提交也将保留在历史记录中。

# Add a 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


5
这刚刚节省了我很多工作时间。我使用了“git reflog --date=iso”命令来查看每个条目的日期/时间,因为没有时间戳,我无法确定。 - MetalMikester
1
对我来说,在 git reset --hard <commit_id> 中,删除 HEAD 就可以了!赞一个图形表示!! - reverie_ss
1
只是提一下:如果你知道分支名称,git reflog <branchname> 可以非常有用,因为你可以看到一个分支的更改。 - Markus Schreiber
3
好的解释。我倾向于在想要将提交带到的分支上使用git cherry-pick <dangling-commit-hash>将其放置在那里。 - Emile Vrijdags

59

使用git fsck命令还有一种方法可以到达已删除的提交。

git fsck --lost-found
这将在最后一行输出类似下面的内容:
dangling commit xyz

我们可以使用其他答案中建议的 reflog 来检查它是否是相同的提交。现在我们可以执行 git merge

git merge xyz

注意:
如果我们已经运行了git gc命令并删除了对悬空提交的引用,则无法使用fsck获取提交。


6
如果你最近没有指向特定的提交(比如在获取分支后意外重置该分支),那么这是唯一可行的解决方案。 - TamaMcGlinn

17

今天我遇到了这个问题,下面是对我非常有帮助的解决方法。我的答案与 @Amber 的答案非常相似。

首先,我运行了git reflog命令并搜索了那个特定提交的哈希值,然后只需复制该哈希值并从该分支运行git cherry-pick <hash> 命令。 这将会把丢失提交中的所有更改合并到当前分支,并恢复我的GIT操作信心。

祝愉快!


17

首先使用git reflog命令列出所有提交记录,包括已经丢失的提交。

git reflog

使用 git log HEAD@{your_commit_number} 命令来查找你需要的提交记录。例如:

git log HEAD@{17}

在使用 git checkout HEAD@{your_commit_number} 找到提交后,进行检出

git checkout HEAD@{17}

您可能需要添加并提交您的更改

git add .
git commit -m"quuck_fix"

接下来,您需要创建一个临时分支将提交恢复到您的分支上。

git branch temp

最后,您将结帐到现有分支,然后合并临时分支。

#git checkout <your_existing_branch> e.g

git checkout main
git merge temp

15

试试这个,这将展示在一段时间内 Git 记录的所有提交

git reflog

使用以下命令找到你想要的提交

git log HEAD@{3}
或者
git log -p HEAD@{3}    

然后检查一下它是否正确:

git checkout HEAD@{3}
这将为该提交创建一个分离的 HEAD。如果需要,请添加和提交任何更改。
git status 
git add
git commit -m "temp_work" 

现在,如果您想将提交恢复到一个分支,比如主分支,您需要给它命名,切换到主分支,然后将其合并到主分支。

git branch temp
git checkout master
git merge temp

这里还有一个链接,专门介绍 Git 教程网站上的 reflog: Atlassian Git 教程


9
如果你使用IntelliJ IDE工具,而且用git reflog不能找到你的提交记录,那么你可以通过右键点击项目根目录 -> 本地历史 -> 显示历史记录来撤销你的更改。
我在执行git rebasegit push -f时出现问题,但是这个方法真正拯救了我,因为该提交记录已经从本地和远程仓库中删除。
希望这能解决一些人遇到的问题。
祝好!

嘿,我正在使用VS,我有一个提交的 commit id 是 ec101349,我该如何使用这个 id 来筛选我的 reflog?这样做有可能吗? - d0rf47
我之前不知道这个功能,这个功能真是帮我省了几个小时的工作! - undefined

2

最安全的方法是

  1. 查找要恢复的状态的提交头编号

git refloggit reflog | grep your_phrase

  1. 从它创建新分支

git checkout -b your-branch HEAD@{<your-number>}


-21

很遗憾,Git太不可靠了 :( 我刚刚丢失了两天的工作 :(

在提交之前最好手动备份任何东西。我刚刚执行了“git commit”,但是Git没有任何提示就摧毁了我的所有更改。

我吸取了教训 - 下次先备份,然后再提交。永远不要相信Git。


2
你能否请看一下基本的git教程?例如:https://product.hubspot.com/blog/git-and-github-tutorial-for-beginners我不想冒犯你,但你还没有掌握它。你可以相信git,只需要学会如何使用它。 :) - F. Norbert
我已经使用 Git 好几年了。这是 Git 中的一个 bug,而不是与用户相关的问题:我像往常一样执行了 Git commit,但它却毁掉了我的所有东西,所以我无法看到所有的更改。Git log 什么也没显示,Git 回到了更改之前的状态。Git 甚至没有报告任何错误。我猜 Git 在提交过程中某个地方崩溃了。 - Ziv Barber
除非“nano”崩溃,否则我从不丢失工作。你可能有我们没有的问题,或者你可能在不知情的情况下出了问题,我已经做过很多次;D - TheTechRobo the Nerd
另外,如果git在提交过程中崩溃,它不会撤销您所有的更改 - 最坏的情况是它会创建一个损坏的提交,您可以还原。你确定没有使用 git stash; git stash drop 命令吗? - TheTechRobo the Nerd
2
Git是全球业界标准的版本控制工具。从大型企业到学生,每个人都在使用它。你真的认为每个人都会使用一个有如此多漏洞的工具,甚至无法完成它唯一应该做的事情吗? - Dino Prašo
可能不是 Git 的错。 :-) 然而,上述解决这个简单问题的各种方法的多样性和复杂性表明,在尝试任何棘手的操作之前,最好备份整个本地仓库。恢复备份比尝试理解问题和可能的解决方案要容易得多。我希望昨晚在搞砸我的变基之前就这样做了。 :-) - Denis Howe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接