为什么git checkout FILE不重新应用未提交的更改?

时间:2018-11-28 02:16:22

标签: git

git checkout COMMIT将您的当前状态切换到COMMIT所指向的状态,并重新应用所有未提交的更改(如果您未提交的更改与您要切换到的COMMIT不兼容,甚至会失败。

此功能与git checkout COMMIT FILE形成鲜明对比,后者只是盲目地丢弃FILE的任何未提交的更改(没有任何警告),并将FILE的状态“强制”为{{1 }}。

我的问题是,为什么不一致? (可能涉及历史/设计决策)

当然可以这样做,以使COMMIT尝试对git checkout COMMIT FILE重新提交未提交的更改,如果失败则正常地失败。尤其是由于FILE通常作为“安全”操作进行销售。如果有的话,git checkout的当前行为应该是git checkout COMMIT FILE的行为。 (顺便提一下,git reset --hard COMMIT FILEgit reset COMMIT的行为也留有改善一致性的余地...)

2 个答案:

答案 0 :(得分:3)

  

git checkout commit将您的当前状态切换为 commit ...

指向的状态

是的,有点。实际上,它实际上要复杂得多。

  

...并重新应用所有未提交的更改(如果您未提交的更改与要切换到的 commit 不兼容,则失败甚至会失败)。

它实际上没有做第一部分(但是做了第二部分),这很复杂:git checkout所做的只是the git read-tree documentation calls a two tree merge的一部分。如果您点击此链接,您会看到它列出了21个单独的案例(一个案例#3,有两个子案例,而我忽略了案例#0不可能发生的情况,因此,如果您计算这些子案例和案例#0,您会得到23种不同的案例)。我不会在这里列出所有这些信息,但是它们归结为这个想法:如果Git在切换提交时不必触摸索引和工作树,那就不用了。是什么使您无法提交的更改保持原状:Git不会重新应用,也不会删除任何东西。它只是轻轻地踩着未提交的更改。

反过来,某种(但不是全部)解释了这种根本不同的行为,包括我在内的某些人甚至可以称其为 evil:

  

此功能与git checkout commit file形成鲜明对比,file只是盲目地丢弃 file 的所有未提交的更改(没有任何警告),并“强制” { {1}} commit 中的内容。

对,这是因为Git将您的命令视为请求,以将 file 的索引副本替换为 {{1 }} 。更新的索引副本也被复制到工作树中。

我会(过去曾经)认为这应该是一个单独的Git命令。它恰好由相同的源代码实现,只是因为该源代码充满了“从提交中提取文件,写入索引和工作树”代码。但是commit也是如此,而git reset 一个单独的命令。 1 因此,这充其量是Hysterical Raisins形式的借口

  

当然可以这样做,使得git reset尝试重新应用对 git checkout commit file 的未提交更改,如果失败,则正常失败。

file选项的git checkout中也可以使用...。但是,它不是基于每个文件,而是仅基于整个提交。也就是说,您可以-m,它将您的工作树与目标提交合并(作为三棵树的合并),但是没有git checkout -m commit。要获得这种效果,请从提交中提取文件,选择要提取的合并基本版本,然后对这三个文件使用git checkout -m commit file

git merge-file

如果您问我,但

1 Git的git show other:file > file.other git show base:file > file.base git merge-file file file.base file.other 会将太多无关的功能塞入一个面向用户的命令中。

  

(顺便说一下,resetgit reset commit的行为也为一致性的改善留有余地...)

好的,也许你做到了! :-)

答案 1 :(得分:1)

当您检出一个提交时,git不会“重新应用”更改。当且仅当检出不影响文件时,它才会保留修改的工作树和索引副本不变-即,您要检出的提交中的文件版本与预检出HEAD中的文件版本相同承诺。但是,如果它必须完全触摸文件,并且该文件的索引或工作树副本已修改,则结帐将中止。

当您checkout特定路径时,该路径会被破坏而不会发出警告,因为这是签出特定路径的定义和目的。您之所以说git checkout -- myfile.txt是因为您还原myfile.txt,实际上,如果您想还原git status,则建议使用myfile.txt命令。请记住,为了与“签出提交”行为保持一致, still 不会尝试重新应用(合并)本地更改。它只会拒绝对任何已修改的文件进行检出。 (公平地说,这没有多大意义。)

表面上的不一致是不幸的选择,它使用相同的关键字进行两个完全不同的操作的几种症状之一。虽然这些操作中的任何一个都可以合理地命名为“ checkout”,但这是我真的不喜欢git的少数几个决定之一,因为它同时使用了该名称。