题 在Git中撤消一个文件的工作副本修改?


在最后一次提交之后,我在工作副本中修改了一堆文件,但我想撤消对其中一个文件的更改,就像将其重置为与最近提交的状态相同。

但是,我只想撤消仅仅一个文件的工作副本更改,而不是其他任何内容。

我怎么做?


1297
2018-03-28 05:09


起源




答案:


您可以使用

git checkout -- file

没有这个你就可以做到 -- (正如nimrodm所建议的那样),但如果文件名看起来像分支或标签(或其他修订标识符),它可能会混淆,所以使用 -- 是最好的。

您还可以查看文件的特定版本:

git checkout v1.2.3 -- file         # tag v1.2.3
git checkout stable -- file         # stable branch
git checkout origin/master -- file  # upstream master
git checkout HEAD -- file           # the version from the most recent commit
git checkout HEAD^ -- file          # the version before the most recent commit

1793
2018-03-28 06:12



HEAD和HEAD ^之间有什么区别? - hasen
HEAD是当前分支上最近的提交,HEAD ^是当前分支上的提交。对于您描述的情况,您可以使用git checkout HEAD - filename。 - Paul
简而言之,“git checkout sha-reference-filename”,其中sha-reference是对提交的sha的引用,以任何形式(分支,标记,父级等) - Lakshman Prasad
注意:如果文件已经暂存,则需要先重置它。 git reset HEAD <filename> ; git checkout -- <filename> - Olie
@gwho是的,你可以做到 HEAD^^ 对于最近的2次提交,或者 HEAD^^^ 3次提交回来。你也可以使用 HEAD~2, 要么 HEAD~3,如果你想要更多的提交,这会更方便 HEAD^2 表示“此提交的第二个父母”;因为合并提交,提交可以有多个以前的提交,所以用 HEAD^ 一个数字选择那些父母,而与 HEAD~ 一个数字总是选择第一个父节点,但返回的提交数量。看到 git help rev-parse 更多细节。 - Brian Campbell


git checkout <commit> <filename>

我今天用这个是因为我意识到我的favicon已被覆盖了几次提交,当时我升级到drupal 6.10,所以我不得不把它取回来。这是我做的:

git checkout 088ecd favicon.ico

115
2018-03-28 10:25



除了滚动抛出吨“git log --stat”输出之外,我如何得到(先前删除的文件)的提交? - Alex
IMO通过命令行扫描gits日志并找到正确的文件很困难。使用GUI应用程序会更容易,例如 sourcetreeapp.com - neoneye
git log --oneline <filename> 将为您提供更紧凑的日志,并且只包含对特定文件的更改 - rjmunro
或者,你可以使用 git reflog <filename> - ygesher


只是用

git checkout filename

这将使用当前分支中的最新版本替换filename。

警告:您的更改将被丢弃 - 不保留备份。


101
2018-03-28 05:55



什么是 - 为? - Patoshi パトシ
@duckx是从文件名中消除分支名称的歧义。如果你说 git checkout x 和x恰好是一个分支名称和一个文件名,我不知道默认行为是什么,但我认为git将假设你想切换到分支x。当你使用 -- 你说以下是文件名。 - hasen
ic谢谢你清理它。每个人都只是假设你知道什么 - 当他们向你展示例子时。而且它也不是你可以轻松谷歌的东西。 - Patoshi パトシ
看起来答案已被编辑以删除 -- 从中。虽然仍然正确,正如@hasen指出的那样,如果文件名和分支名称之间存在歧义,您可能会在这里遇到非常不受欢迎的行为! - BrainSlugs83
没有,我喜欢它的样子 --, 好,易于。当你使用文件名命名分支时,某些地方一定有不好的想法...... - Marco Faustinelli


如果您的文件已经暂存(在编辑文件后执行git add等操作时),则取消暂停您的更改。

使用

git reset HEAD <file>

然后

git checkout <file>

如果尚未上演,请使用

git checkout <file>

52
2017-07-03 05:46



哈哈,这比被接受的哈哈更有帮助。很容易忘记已经上演的变化和没有变化的变化,因此重置有所帮助。虽然之前我也试过“git reset --hard”,但它没有做“git reset HEAD”所做的事情。我想知道为什么? - Arman Bimatov


如果您只想撤消先前提交对该文件的更改,您可以尝试这样做:

git checkout branchname^ filename

这将检出上次提交之前的文件。如果你想再提交一些提交,请使用 branchname~n 符号。


15
2018-03-28 05:25



这不会从提交中删除更改,它只会将diff应用于HEAD上的版本。 - FernandoEscher
虽然如此,原始海报只是想恢复他的工作副本修改(我认为),而不是从上次提交中恢复更改。原始海报的问题有点不清楚,所以我可以理解这种混乱。


我总是对此感到困惑,所以这里有一个提醒测试用例;让我们说我们有这个 bash 要测试的脚本 git

set -x
rm -rf test
mkdir test
cd test
git init
git config user.name test
git config user.email test@test.com
echo 1 > a.txt
echo 1 > b.txt
git add *
git commit -m "initial commit"
echo 2 >> b.txt
git add b.txt
git commit -m "second commit"
echo 3 >> b.txt

此时,更改不会在缓存中暂存,因此 git status 是:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

如果从这一点开始,我们这样做 git checkout结果如下:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

相反,我们这样做 git reset,结果是:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

所以,在这种情况下 - 如果没有上演更改, git reset 没有区别,而 git checkout 覆盖更改。


现在,让我们说上面脚本的最后一个更改是暂存/缓存,也就是说我们也做了 git add b.txt 最后。

在这种情况下, git status 在这一点上是:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   b.txt

如果从这一点开始,我们这样做 git checkout结果如下:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

相反,我们这样做 git reset,结果是:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

所以,在这种情况下 - 如果更改是暂停的, git reset 基本上会将分阶段的变化变成未分阶段的变化 - 同时 git checkout 将完全覆盖更改。


7
2018-02-09 08:56





我使用SHA id恢复我的文件,我做的是什么 git checkout <sha hash id> <file name>


5
2018-05-22 20:39





我通过git bash完成了:

(use "git checkout -- <file>..." to discard changes in working directory)

  1. Git状态。 [所以我们看到一个文件被修改过。]
  2. git checkout - index.html [我在index.html文件中已更改:
  3. git状态 [现在删除了这些更改]

enter image description here


3
2018-04-10 14:54



在SO的8年里,我以前从未见过这样的事情。采取你的upvote! - Joshua Pinter


如果您尚未推送或以其他方式共享您的提交:

git diff --stat HEAD^...HEAD | \
fgrep filename_snippet_to_revert | cut -d' ' -f2 | xargs git checkout HEAD^ --
git commit -a --amend

2
2018-04-30 19:45