为什么 `git checkout <branch> <file>` 会进行更改?

Why does `git checkout <branch> <file>` stage the change?

如果我从一个干净的工作树和 运行 git checkout <branch> <file> 开始,其中 <branch> 有这个文件的不同版本,我最终得到一个暂存的而不是未暂存的更改.

这是什么原因?这是否只是为了与其他命令(如 git mv)保持一致,您希望对这些命令进行暂存更改?使用git checkout解决合并冲突时是为了方便吗?还是有其他原因?

这对我来说似乎有点奇怪,因为仅使用 git checkout <branch> <file> 并没有提供任何迹象表明我是否打算提交更改。

这确实是 Git 作者选择展示的实现细节。

Git 不能——或者更确切地说,在某一时刻,不能 不能——将文件直接从存储库读取到工作树中。它必须(或必须)首先通过中介传递它们:它必须复制它们,或者至少复制它们的生命统计数据,1 到其他地方。只有这样才能 Git 将数据复制到工作树文件。2 "somewhere else" 是一个 index 条目。索引也称为暂存区。

当您 git checkout 整个提交时,这就是您想要的。因此,首先复制到索引然后才复制到工作树的内部限制实际上是一个加号。因此,这种首先复制到索引中,然后才复制到工作树中的机制被嵌入到实现中。然后,最终,面向用户的 git checkout 前端获得了检出单个文件或一小部分文件的能力......并且它继续这样做 指数。实现细节成为一个文档化的特性。

请注意,有时,索引在冲突合并期间用作辅助区域。在这种情况下,对于某些文件 F,最多有 三个 个条目,在编号的插槽 1(基本)、2(--ours), 和 3 (--theirs), 而不是普通零槽中的一个条目。如果是这样,您可以将三个索引槽条目中的任何一个提取到工作树中,而不会干扰索引。但是,如果您使用 git checkout 从其他提交或树中提取文件,则 Git 会将文件复制到索引中,并将其写入槽零。这具有 删除 更高编号的插槽的副作用,解决了合并冲突!


1主要是hash ID。由于 ,Git 必须将提交哈希解析为树哈希,然后搜索各种树以找到感兴趣的文件 is/are,以便它可以获得 blob哈希。不过,索引条目本身也有更多数据,包括工作树文件的 stat 结构数据,使 Git 运行得更快。

2您仍然可以使用此工作流程,方法是使用 git read-tree 将树复制到索引中,然后使用 git checkout-index 复制工作树的索引。最初,Git 由一堆 shell 脚本组成,例如 git-checkout,包裹着一些基本的 C 代码片段,例如 git-read-tree。 (名字都是这样连字符的,没有前端git命令。)

简答

  1. git checkout 总是 将索引中的项目复制到工作树中。

  2. 如果您指定的提交不是您所在的提交(例如另一个分支的 HEAD),结帐 将始终先 从提交到索引中。

  3. 索引中与 HEAD 不同的任何内容都将显示为 "staged change"。这是根据定义。

另见