Git:放弃自某次提交以来对单个文件的所有更改

时间:2013-11-21 17:18:07

标签: git git-checkout

我想“撤消”对一个特定文件的所有本地更改,即在origin / master处返回此文件的状态:

  • 如果它存在并被修改=>恢复旧版本。
  • 如果已添加=>删除文件。
  • 如果已删除=>重新添加文件。
  • 如果重命名=>重命名为旧名称。

git checkout origin/master -- path/to/file.txt适用于已修改的文件,但特别是对于已添加但因此在origin / master中不存在的文件(“没有此类文件或目录”)而失败。

如果需要,我可以依赖提交只改变一个文件。 (那么也许我可以解析来自git log --follow --oneline --decorate origin/master.. path/to/file.txt的SHA,然后还原(或重新设置/删除)这些提交。但这似乎是手动和hacky,我更喜欢更具声明性的解决方案......)

更新:为了澄清,已经提交了本地更改。可能有多个提交对文件进行了更改。

3 个答案:

答案 0 :(得分:3)

我假设您已经在本地进行了所有更改...

我会这样做:

git reset HEAD~

git checkout path/to/file.txt

git add -A

git commit -m "commit message"

这也很容易作为脚本添加到bash / other shell中。

如果有多个提交对文件进行了更改,您可以执行以下操作:

git log -p path/to/file.txt

然后找到要将文件还原到的提交。然后,只恢复该文件:

git checkout <sha-from-above> path/to/file.txt

请参阅:Reset or revert a specific file to a specific revision using Git?

答案 1 :(得分:3)

除重命名问题外,您首先建议的一般方法(git checkout <rev> -- <path>)是明显的正确方法。如果失败,则必须添加该文件,因此您只需将“失败”作为“删除指示”。

再次出现重命名问题。

在这里,您可以使用您建议的git log --follow方法。关注,观看重命名操作,并收集它们。您可以使用--name-status来观察重命名,例如:

$ git log --follow --oneline --name-status -- builtin/var.c
f9bc573 ident: rename IDENT_ERROR_ON_NO_NAME to IDENT_STRICT
M       builtin/var.c
c2e86ad Fix sparse warnings
M       builtin/var.c
2bc8c1a var: run setup_git_directory_gently() sooner
M       builtin/var.c
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
R100    builtin-var.c   builtin/var.c
64778d2 Make 'git var GIT_PAGER' always print the configured pager
M       builtin-var.c
9fabb6d Fix 'git var' usage synopsis
M       builtin-var.c
55b6745 make "git var" a built-in
R096    var.c   builtin-var.c
6361824 Teach git var about GIT_PAGER
M       var.c
[snip]

(我在git源代码上运行了以上内容)。这里有一个相当大的打嗝,因为你必须知道文件的 new 名称,而不是旧名称,但也许这就是问题。

按照重命名后,您可以git checkout <rev> -- <path-by-name-at-the-time>--name-status部分还将显示文件是否在历史版本之前的转录中创建(状态将为A,已添加),您将知道要删除该文件。

省略了“文件被删除”的情况,这与您知道文件新名称的基本想法相冲突。删除文件foo后,它首先只有一个旧名称。

据我所知,没有好办法处理这最后一个案例。您可以假设,如果{“1}}存在于”旧“提交中并且您没有新名称,则必须将其删除;但是如果跟随重命名的想法是以名称开头,那么这在git中不起作用:foo选项仅在从最近的历史记录开始并在时间上回溯时才有效。 / p>

答案 2 :(得分:2)

我设法用这些命令获得了我的预期结果:

$ git reset origin/master path/to/file.txt
$ git commit -m "undo changes to path/to/file.txt"
[master ab4553d] undo changes to path/to/file.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 path/to/file.txt
$ git reset --hard
HEAD is now at ab4553d undo changes to path/to/file.txt

粗略测试后,这可以按预期添加/删除/修改文件。对于重命名的文件,需要为旧名称和新名称执行git reset

使用git reset而不是git checkout是否有不利之处?