为什么 `git checkout <branch> <file>` 会将更改暂存?

24
如果我从一个干净的工作树开始,并运行 git checkout <branch> <file> 命令,其中 <branch> 拥有该文件的不同版本,则会出现暂存而不是未暂存的更改。
这是为什么呢?这是否只是为了与其他命令(如 git mv)保持一致,您期望该命令会暂存更改?还是为了方便使用 git checkout 解决合并冲突?或者还有其他原因吗?
这对我来说似乎有点奇怪,因为仅仅使用 git checkout <branch> <file> 并没有任何指示我是否计划提交更改。

git checkout <branch> <file> 中,分支被解析为树,即该分支最新提交的树。此命令首先针对给定文件更新索引,然后将工作树中的文件更新为与索引版本匹配的版本。因此,现在工作目录和索引中的文件已更新为相同。它与 git add <file> 相同。有关更多信息,请参阅 git checkout --help - ElpieKay
可能是为什么有时候checkout会暂存文件?的重复问题。 - Keith Pinson
2个回答

16

这实际上是Git作者选择让其显示出来的一些实现细节。

Git不能直接从存储库读取文件到工作树中,或者更确切地说,曾经不能。它必须先将它们通过某个中介复制到其他地方,或者至少复制它们的关键信息1。然后Git才能将数据复制到工作树文件中。2“其他地方”是一个索引条目。该索引也称为暂存区。

当您使用git checkout检查出整个提交时,无论如何都会执行此操作。因此,将文件首先复制到索引中,然后再复制到工作树中的内部限制实际上是一个加号。因此,这种机制被嵌入到实现中。随后,基于用户的git checkout前端获得了检出一个单独文件或某个小的文件子集的功能...并且它继续通过索引执行此操作。这个实现细节变成了一个记录下来的功能。

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


1主要的标识是哈希ID。正如ElpieKay在评论中提到的,Git必须将提交哈希解析为树哈希,然后搜索各个树以找到感兴趣的文件或文件集,以便获得Blob哈希。索引条目本身也有更多数据,包括用于使Git快速运行的工作树文件的stat结构数据。

2您仍然可以使用此工作流程,通过使用git read-tree将树复制到索引中,然后使用git checkout-index将索引复制到工作树中。最初,Git由一堆像git-checkout这样的shell脚本包装了一些基本的C编码部件,例如git-read-tree。(名称都像这样连字符号分隔,并且没有前端git命令。)


4

简短回答

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

  2. 如果您指定了一个与当前提交不同的提交(例如另一个分支的HEAD),checkout 将始终首先 将该提交的项目复制到索引中。

  3. 索引中与HEAD不同的任何内容都将显示为“已暂存的更改”。这是定义。

另请参阅从分支检出文件而不更改索引的Git命令


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