git reset --hard HEAD和git checkout <file>的区别

7
我有一个名为foo.py的文件。我对工作目录进行了一些更改,但是还没有将这些更改添加到暂存区或提交更改。我知道可以使用git checkout foo.py来取消这些更改。我还了解到可以使用git reset --hard HEAD来重置您的工作目录、暂存区和提交历史记录以匹配最新提交。
在我的情况下,我的更改仍然在工作目录中,是否有理由更喜欢使用其中之一呢?
3个回答

6

在我的情况下,我对工作目录中的更改还没有提交,是否有理由更喜欢使用其中一种方法?

没有,因为它们都可以完成同样的任务。

总的来说(但不是在我的特定情况下),是否有理由更喜欢使用 [git checkout -- path/to/file] 而不是 [git reset --hard]?

是的:这只会影响一个文件。如果你习惯于使用 git reset --hard 撤消对一个文件的更改,并且你还有工作树和/或暂存的其他文件的更改,那么如果你运行 git reset --hard,你可能无法恢复这些更改,除非手动重建它们。

注意,还有第三种命令:git checkout HEAD path/to/file。与不带 HEAD 的命令相比(用 -- 代替1),两者的区别在于带有 HEAD 的命令意味着首先将永久不可更改的提交中的文件版本复制到索引/暂存区,然后再复制到工作树。而带有 -- 的命令意味着将索引/暂存区中的文件版本复制到工作树。


1使用 -- 的原因是确保 Git 永远不会将文件名与其他任何内容混淆,例如分支名称。例如,假设你将一个文件命名为 master,那么 git checkout master 是什么意思?它应该检出分支 master 还是提取文件 mastergit checkout -- master 中的 -- 使其对 Git 和人类都清晰明了,表示“提取文件 master”。


总结或需要记住的要点

每个文件始终有三个活动副本:

  • HEAD 中的一个;
  • 索引/暂存区中的一个;
  • 工作树中的一个。

git status 命令查看这三个副本,首先比较 HEAD vs. 索引,这给 Git 提供了“要提交的更改列表”,然后比较索引 vs. 工作树。第二个比较给 Git 提供了“未暂存的更改列表”。

git add 命令将文件从工作树复制到索引中。

git checkout 命令有多种操作模式,可以从 HEAD 复制到索引再到工作目录,也可以直接从索引复制到工作目录。因此,它有一定的复杂性:
  • git checkout -- path/to/file:将文件从索引复制到工作目录,不考虑 HEAD 中的内容。如果文件名看起来像分支名称(例如一个名为 master 的文件)或选项(例如一个名为 -f 的文件),则 -- 通常是可选的。

  • git checkout HEAD -- path/to/file:将文件从 HEAD 提交中复制到索引,然后再复制到工作目录。这里的 HEAD 覆盖了索引中的内容,索引又覆盖了工作目录中的内容。除非文件名看起来像选项(例如 -f),否则 -- 通常是可选的。

    最好总是使用 --,养成好习惯。

git reset 命令也很复杂(它有许多操作模式)。
(这并不是说 git checkout 很简单:它也有许多操作模式,可能太多了。但我认为 git reset 至少稍微复杂一些。)

不是在抱怨,但回想起来,我的答案看起来像是你的 tl;dr... 更不好的是我先发了它 ;-) - NichtJens
HEAD提交复制到索引,然后到工作树。我可以将“复制到索引”想象为在将文件重置为最后一次提交的版本后将其添加到暂存区(==索引)吗?由于如果工作树版本等于最后提交的版本,则git add不会执行任何操作,所以为什么要强调这一步骤的重要性? - johk95
1
@johk95:索引包含(模式,名称,哈希)元组,所以“复制到索引”只是找到索引中正确的插槽并更新3元组的哈希部分。之所以要说明这一步骤,是因为索引总是包含下一次提交中的每个文件。在冲突合并解决期间,索引将对每个文件包含最多3个条目,并且将复制到索引中会删除所有非零阶段条目,并用单个零阶段条目替换它们,标记冲突已经解决。git checkout <commit> --<path> 操作会执行此操作。有一个新的命令[cont]。 - torek
自Git 2.23以来,有一个新命令git restore,可以将文件从提交或索引复制到您的工作树,并且在从提交复制时可以绕过这种写入索引模式。如果您希望避免“解决正在进行的冲突”的副作用,则可以使用此功能。 - torek
1
@johk95:值得一提的是,git add也可以通过相同的过程解决冲突:它会写入索引。当工作树和最后提交版本都匹配时,“写入索引”操作只会将三个非零阶段条目合并为一个零阶段条目,或者仅将原始哈希 ID 写回(这将保持索引条目不变,并且如果没有更改其他索引条目,则意味着 Git 甚至不必费心写回索引数据)。 - torek
显示剩余2条评论

4

0

在您的限制下,没有区别。但如果有分阶段的更改,则会有区别:

reset 会还原已分阶段的更改。

checkout 则不会……


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