“git add file”和“git checkout - file”是否对称?

时间:2013-12-05 10:08:36

标签: git git-checkout git-add

我对git add filegit 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之后,暂存区域中的文件将文件状态更改为暂存区域中的先前状态吗?

2 个答案:

答案 0 :(得分:6)

这几乎是,但不完全是对称的。

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

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

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

在repo中,目录树存储为“tree”类型的对象。树对象包含名称列表(如.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_namegit hash-object -t blob)。
  2. 内容的哈希值
  3. 如果该对象尚未在回购中,请将其写入回购(使用-w选项hash-object)。
  4. 更新.git/index(或$GIT_INDEX_FILE),使其名称file_name下的映射到git hash-object的名称。这始终是“阶段0”条目(这是正常的,不合并冲突的版本)。
  5. 因此,文件并没有真正“进入”临时区域,它真的“在”回购本身!暂存区域中的内容是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)。如果没有stage-0条目,则使用“unmerged”消息输出错误,除非给出-m--ours--theirs个选项。使用-m将“取消合并”文件(删除第0阶段条目并重新创建冲突的合并 2 ),同时--ours--theirs离开任何阶段0条目到位(已解决的冲突仍然解决)。

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

    3. 因此,短版本是“是和否”:git checkout有时会修改索引,有时只使用它。但是,文件本身永远不会存储在索引中,只存储在repo中。如果您git add一个文件,再更改一次,再次git add,这就会留下git fsck会发现的“悬空blob”:一个没有引用的对象。


      1 我故意省略索引中的很多其他东西,以使git表现良好,并允许--assume-unchanged等。(这些与添加无关/ checkout动作在这里。)

      2 此重新创建尊重对merge.conflictstyle的任何更改,因此,如果您决定使用diff3输出并且已经存在冲突的合并而没有diff3样式,您可以更改git配置并使用git checkout -m来获得与新样式合并的新工作目录。

答案 1 :(得分:0)

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