如何将文件重置或还原到特定版本?

5680
如何将已修改的文件恢复到特定提交哈希的先前版本(我通过git loggit diff确定)?

20
回滚后,检查 git diff 时不要忘记加上 --cached 参数。 - Geoffrey Hale
7
当我谷歌搜索我的问题时,发现了你的问题。但是当我阅读解决方案后,我检查了我的日志并发现我将这些更改作为独立提交进行了更改,所以我对该提交进行了git还原,而其他一切都像我想要的那样保留下来。这不是解决方案,只是另一种有时可以做到的方法。 - sudo97
我使用以下手动解决方案:$ git revert <commit>,然后取消隐藏所需更改,然后将这些所需更改上传到新提交中。 - Intenzion
36个回答

7645
假设您想要的提交哈希值为:

git checkout c5f567 -- file1/to/restore file2/to/restore

git checkout 的手册提供了更多信息。

如果你想回滚到 c5f567 之前的提交,可以附加 ~1(其中 1 是你想要回退的提交数,可以是任何数字):

git checkout c5f567~1 -- file1/to/restore file2/to/restore

顺便提一句,我一直对这个命令感到不舒服,因为它既用于普通的事情(在分支之间切换),又用于不寻常、破坏性的事情(放弃工作目录中的更改)。

有关命令中--的含义,请参阅在Git中,--表示什么?


还有一个新的git restore命令,专门用于恢复已修改的工作副本文件。如果您的git足够新,可以使用此命令,但文档附带了警告:

此命令是实验性的。行为可能会发生变化。


14
有没有办法把那个倒过来,变成它之后的版本? - aliteralmind
19
抱歉,Git 历史记录快捷符号只能向后查看历史记录。 - Greg Hewgill
71
如果你要为 abcde 使用一个分支名(例如develop),则需要执行git checkout develop -- file/to/restore命令来还原文件(请注意双横线)。 - Ohad Schneider
12
实际上,是有一种方法可以做到:使用命令"git log --reverse -1 --ancestry-path yourgitrev..master",并使用适当的选项只获取git版本号。--ancestry-path将在两个提交之间“画一条线”,-1将只显示一个版本,而--reverse将确保发出的第一个条目是最旧的。 - Chris Cogdon
14
个人觉得输入 HEAD^ 比输入 HEAD~1 更容易。 :) - juzzlin
显示剩余22条评论

748

使用diff命令可以快速查看对文件所做的更改:

git diff <commit hash> <filename>

然后要将特定文件还原到该提交中,请使用reset命令:

git reset <commit hash> <filename>

如果您有本地修改,则可能需要使用--hard选项。

管理路标点的好工作流是使用标签来清晰地标记时间线上的点。我无法完全理解您的最后一句话,但您可能想要从以前的某个时间点分叉一个分支。要做到这一点,请使用方便的checkout命令:

git checkout <commit hash>
git checkout -b <new branch name>

当您准备合并这些更改时,可以将其与主干进行变基:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

9
'git checkout <commit hash>' 命令已经帮我找到了我需要的旧版本的项目,非常感谢 Chris。 - vidur punj
75
相较于使用 git reset,对于我来说,使用 git checkout <commit hash> <filename> 恢复文件的效果更好。 - Motti Strom
5
我需要一个早期版本的单个文件,因为我用错误的复制/粘贴覆盖了150行。git checkout <commit hash> <filename> 对我有用。在我看来,这不应该是被接受的答案。git reset 不起作用。 - harperville
31
无法使用git reset来重置单个文件,你会收到错误提示fatal: Cannot do hard reset with paths - slier
18
Slier说:你不能使用git reset --hard <commit hash> <filename>。这会出现错误信息fatal: Cannot do hard reset with paths. Motti Strom建议使用git checkout <commit hash> <filename> - Hawkeye Parker
显示剩余8条评论

429

您可以使用任何有关git提交的引用,包括SHA-1哈希值,以方便为准。重点在于命令看起来像这样:

git checkout [commit-ref] -- [filename]


36
这个答案有“--”,而被采纳的答案没有,“--”,它们有什么区别? - 2rs2ts
105
在git中,文件列表前的“--”告诉git所有接下来的参数都应该被解释为文件名,而不是分支名称或其他任何内容。这是一个有用的消歧工具。 - foxxtrot
66
“--”不仅是 Git 约定,而且在*nix命令行的各个地方都可以找到。 rm -- -f(删除名为“-f”的文件)似乎是典型的示例。更多细节请参见此处 - Hawkeye Parker
7
补充一下@HawkeyeParker所说的,rm命令使用getopt(3)解析其参数,getopt是用于解析命令参数的命令。http://www.gnu.org/software/libc/manual/html_node/Getopt.html - Devy
2
@Honey 是的,这就是我的意思,也许并不常见。我在各个地方都看到过这个例子,可能只是为了让它更加难忘:rm -f 众所周知是很可怕/危险的。但是,重点是,在 *nix 中,文件名可以以 '-' 开头,这会使各种命令行解释器感到困惑,当它们看到 '-' 时,期望后面跟着一个命令选项。它可以是任何以 '-' 开头的文件;例如,"-mySpecialFile"。 - Hawkeye Parker
显示剩余4条评论

374
git checkout -- foo

这将重置foo到HEAD。你还可以:

git checkout HEAD^ foo

回滚到上一个版本等。


17
建议使用语法 git checkout -- foo,以避免任何错误,如果 foo 是特殊的(如一个名为 -f 的目录或文件)。在Git中,如果不确定,请始终在所有文件和目录前加上特殊参数 -- - Mikko Rantalainen
13
对Mikko评论的补充说明:--不是git的命令,也不是git特有的。它是bash内置命令,用于表示命令选项的结束。你也可以在许多其他bash命令中使用它。 - matthaeus
22
@matthaeus 这也不是仅限于Bash或任何shell特性。它是许多不同命令中实现的一种约定(并由getopt支持)。 - Greg Hewgill
5
不,"--" 不是 Bash 内置的特殊单词。但它是许多命令行解析器支持的常见约定,并被许多 CLI(包括 git)使用。 - Emil Lundberg
Windows把 ^ 视为转义字符,因此 git checkout HEAD~1 foo 对我来说可行。 - wolfram77

158

从git v2.23.0开始,有一种新的git restore方法,它应该承担了git checkout的部分责任(即使被接受的答案中提到git checkout相当令人困惑)。请看在GitHub博客上对更改的重点介绍。

该命令的默认行为是使用来自source参数的内容恢复工作树的状态(在您的情况下,将是提交哈希)。

所以根据Greg Hewgill的答案(假设提交哈希为c5f567),命令应该是这样的:

git restore --source=c5f567 file1/to/restore file2/to/restore

或者,如果你想恢复到 c5f567 之前某个提交的内容:

git restore --source=c5f567~1 file1/to/restore file2/to/restore

14
我猜这个帖子已经没人关注了,但这是正确的“现代”答案。 - Dan
8
这是截至2021年最佳答案。 - sam
正确答案在第5个位置,而所有曾经正确的被点赞最多的答案则排在前面。这并不是Stackoverflow技术筛选能力的光辉范例(社区不应该“点赞7500次”才能让它在默认顺序中出现)。 - JL Peyret
3
您可以使用“-s commit”代替“--source=commit”。 - 12Me21
@JLPeyret 更糟糕的是,目前这个回答每单位时间获得的投票数比这个回答多,所以它将永远无法超越。但是,根据现在的情况来看,这个回答有望在250年内超过第二高票回答,并且当它成为第二高票回答时,可能会开始获得比第一高票回答更多的赞同票? - gerrit
@JLPeyret 更糟糕的是,目前情况下,得票最多的答案每单位时间获得的票数比这个答案多,所以它永远无法超越。但是,这个答案看起来有望在250年内超过得票第二多的答案,也许当它成为第二多的答案时,它可能开始获得比第一多的答案更多的赞同票? - undefined

154

如果要恢复到最近提交的版本,这是最常用的操作,可以使用这个更简单的命令。

git checkout HEAD file/to/restore

2
这两个命令有什么区别:git checkout HEAD file/to/restore 和 git reset --hard file/to/restore? - Motti Shneor
3
  1. 更易记且更通用的方式。
  2. 在输入文件名之前无需担心按下回车键。
- Roman Susi
这是对“真正”问题的更有效回答。 - ati ince

115

我刚遇到相同的问题,我发现这个答案最容易理解(commit-ref是你想回滚到的日志更改的SHA值):

git checkout [commit-ref] [filename]

这将把旧版本放入你的工作目录中,然后你可以根据需要提交它。


104

如果您知道需要回退多少个提交,可以使用以下命令:

git checkout master~5 image.png

假设你当前在 master 分支上,并且你想要的版本是往前数第 5 次提交。


87

我想我找到了...来源于http://www-cs-students.stanford.edu/~blynn/gitmagic/ch02.html

有时候你只是想回到某个点之前的状态,忘掉所有在这个点之后的更改,因为它们都是错误的。

首先输入:

$ git log

这会显示近期提交的一系列记录和它们的SHA1哈希值。

接下来,输入:

$ git reset --hard SHA1_HASH

可以将仓库恢复到特定的提交(commit)状态,并永久地擦除比这个提交更新的所有提交(commit)记录。


26
Git从不删除任何内容。你以前的提交仍然存在,但是除非有一个分支指向它们,否则它们就不再可达了。使用git reflog仍然可以显示它们,直到你用git-gc清理你的存储库。 - Bombe
5
可能会紧随其后使用git push --force命令。 - bshirley
4
如果您有未提交的更改,执行 git reset --hard 将会导致这些更改丢失。 - Boklucius
2
这个会重置所有文件,而不仅仅是一个特定的文件吗? - aidan
5
“Git从来不会删除任何东西。你旧的提交记录仍然存在,但除非有一个分支指向它们,否则它们将变得不可访问。”- 但是像这样的提交记录在一定时间后会被修剪,因此“Git从来不会删除任何东西”是不正确的。 - Bulwersator
显示剩余5条评论

69

这对我有用:

git checkout <commit hash> file

然后提交更改:

git commit -a

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