5

git add file我对and有以下理解git checkout -- file(但不确定是否正确)。

每当我们使用文本编辑器编辑文件时,我们都会在工作目录中进行。每次我们可以staging area通过执行将文件移动到所谓的git add file_name. 如果我们再次编辑文件(之后git add),我们会更改工作目录中的文件,这样,在工作目录中,文件处于“新”状态,而staging area文件处于“旧”状态。

当我们git add再次使用时,我们将暂存区中的文件带入“新”状态(来自工作目录的状态)。

如果我们这样做git checkout -- file_name,我假设我们从暂存区获取一个文件并使用它来覆盖工作目录中的文件。通过这种方式,我们可以将工作目录中的文件带入“旧”状态。这是对的吗?

我也不清楚,如果我们从暂存区复制或移动文件。换句话说,确实git checkout -- file改变了staging area. 我们可以说git checkout -- file在暂存区中的文件将文件状态更改为暂存区之前的状态吗?

4

2 回答 2

7

它几乎但不完全是对称的。

确实git add file将文件复制到舞台(又名“索引”)。但是,它这样做的方式有点奇怪。

在 git repo 中,所有内容都存储为 git“对象”。每个对象都有一个唯一的名称,即它的 SHA-1(那些 40 个字符的字符串,例如— 来自我这里753be37fca1ed9b0f9267273b82881f8765d6b23的实际情况)。.gitignore该名称是通过计算文件内容的哈希来构造的(或多或少——有一些噱头可以确保您不会从目录树或提交中创建文件,并导致哈希冲突,例如)。Git 假设无论内容如何,​​SHA-1 都是唯一的:没有两个不同的文件、树、提交或带注释的标签会哈希到相同的值。

文件(和符号链接)是“blob”类型的对象。因此,git repo 中的文件被散列,并且在某个地方,git 有一个映射:“文件名为.gitignore”到“散列值753be37fca1ed9b0f9267273b82881f8765d6b23”)。

在 repo 中,目录树存储为“树”类型的对象。树对象包含名称(如.gitignore)、模式、对象类型(另一棵树或 blob)和 SHA-1 的列表:

$ git cat-file -p HEAD:
100644 blob 753be37fca1ed9b0f9267273b82881f8765d6b23    .gitignore
[snip]

提交对象为您(或 git)提供一个树对象,最终为您提供 blob ID。

另一方面,暂存区(“索引”)只是一个文件,.git/index. 该文件包含1名称(以一种有趣的略微压缩的形式,使目录树变平)、合并冲突情况下的“阶段编号”和 SHA-1。同样,实际文件内容是 git repo 中的一个 blob。(Git 不在索引中存储目录:索引只有实际文件,使用扁平格式。)

所以,当你这样做时:

git add file_name

git 这样做(或多或少,我故意掩盖过滤器):

  1. 计算文件file_name( git hash-object -t blob) 内容的哈希值。
  2. 如果该对象尚未在 repo 中,请将其写入 repo(使用-wto 选项hash-object)。
  3. 更新.git/index(或$GIT_INDEX_FILE),使其在名称下具有映射file_name到来自 的名称git hash-object。这始终是“阶段 0”条目(这是正常的、无合并冲突的版本)。

因此,该文件并不是真正“在”暂存区域,而是真正“在”存储库本身!暂存区域中的内容是名称到 SHA-1 映射。

相比之下,git checkout [<tree-ish>] -- file_name这样做:

  1. 如果给定一个<tree-ish>(提交名称、树对象 ID 等 - 基本上 git 可以解析为树的任何内容),则通过将参数转换为树对象来从找到的树中进行名称查找。使用如此定位的对象 ID,将索引中的哈希更新为阶段 0。(如果file_name命名树对象,git 会递归处理树所代表的目录中的所有文件。)通过创建阶段 0 条目,任何合并冲突file_name都是现在解决了。

    否则,在索引中进行名称查找(不确定如果file_name是目录会发生什么,可能是 git 读取工作目录)。将 转换file_name为对象 ID(此时将是一个 blob)。如果没有第 0 阶段条目,则错误并显示“未合并”消息,除非给出-m, --ours,--theirs选项。使用-m将“取消合并”文件(删除阶段 0 条目并重新创建冲突的合并2),同时--ours保留--theirs任何阶段 0 条目(已解决的冲突保持已解决)。

  2. 在任何情况下,如果这还没有出错,请使用因此定位的 blob SHA-1(s)将 repo 副本(或副本,如果file_name命名一个目录)提取到工作目录中。

所以,简短的版本是“是与否”:git checkout有时修改索引,有时只使用它。但是,文件本身永远不会存储在索引中,只会存储在 repo 中。如果您git add是一个文件,请对其进行更多更改,然后再更改git add一次,这会留下 git fsck 将发现的“悬空 blob”:一个没有引用的对象。


1我故意省略了索引中的许多其他内容,以使 git 表现良好,并允许--assume-unchanged等。(这些与此处的添加/签出操作无关。)

2此重新merge.conflictstyle创建diff3尊重diff3git checkout -m.

于 2013-12-05T11:06:02.173 回答
0

当您添加文件时,git add您标记了要提交具有该状态的文件。Git 会记住文件的状态,并在您提交或重置时保持不变。因此,暂存后对文件的每次操作都将使用工作目录中的文件,而不是暂存。
当您运行git checkoutgit 时,只会将未暂存的文件更改为 HEAD 修订版。要将暂存文件更改为您的 HEAD 修订版,您需要运行git reset

于 2013-12-05T10:47:31.720 回答