是否可以在 git 中命名不同批次的暂存文件?

Is it possible to name different batches of staged files in git?

例如,我对分支中的几个文件进行了更改。 然后我使用 git add.

对它们进行分阶段

然后我还有其他的额外改动。 我使用 git add.

再次上演它们

是否可以为每个阶段命名? 谢谢。

简短的回答是否定的。

长答案是 是的,但甚至不要尝试这样做,你只会让自己发疯

要了解原因,让我们看看“暂存区”的实际工作原理。

Git 是关于提交

Git 总的来说,都是关于提交的。尽管提交存储文件,但它与文件无关;这与分支无关,尽管我们将提交组织成我们称之为分支的东西,并且我们使用分支 names(我们也称之为“分支”,尽管它们完全不同——见 Haddock's Eyes 了解更多关于事物、名称和名称名称之间的混淆)...我在哪里?啊是的,我们使用分支名称来 find 提交,因为每个提交的 true 名称是唯一的,但是又大又丑,random-looking, too-difficult-for-humans-to-use 哈希 ID对象 ID (OID).

所以每个 Git 提交都有编号,有一个唯一的编号,表示为一个丑陋的大 hexadecimal string. Git stores every commit—plus a bunch of supporting objects, which also get OIDs—in a big database of "all Git objects". Using the OIDs (hash IDs) as keys, Git can look up the objects nearly instantly in this key-value store,这很棒,除了一些问题:

  • 数据库中没有存储任何内容永远无法更改
  • 一次提交存储每个文件的完整快照(及时冻结,永远)。这些文件 de-duplicated 跨(甚至在)提交以保存 space,这完全可以,因为它们一直被冻结。
  • 每个提交还存储一些元数据,我们将忽略此特定答案。

所以每次提交就像每个文件的永久存档(tar 或 zip 或其他)。但是这些文件以特殊的read-only、Git-only、压缩和de-duplicated形式存储。只有 Git 可以 读取 它们,而且您计算机上的任何内容,甚至 Git 本身都不能覆盖它们。1

这意味着您实际上不能使用提交的文件!这可能会让您想知道它们有什么用。

虽然这个问题的解决方案很简单:当您使用 git switchgit checkoutselect 提交时,Git 读取 文件的冻结副本(Git 可以 做,即使别无他法)并使用它来写入 可用 版本的文件。这些可用版本作为普通的日常文件进入您的工作树。如果这就是 Git 所做的一切,那么事情会非常简单,您一开始就不会问这个问题,但这还不是 Git 所做的全部。


1如果 设法覆盖一个,结果是一个损坏的 Git 数据库:你不能再提取使用该覆盖文件的任何提交。所以 Git 不会那样做; Git 试图阻止其他程序这样做;没有别的应该首先尝试。


Git 的索引又名暂存区

棘手的一点是,当 Git 提取一个提交时,您可以使用它——无论是阅读它,还是稍后构建一个新的提交——Git 复制所有冻结的文件从deep-freeze到Git调用它的索引暂存区。 (Git 对这个东西有第三个名字,称它为 cache,虽然现在这个名字用处不大:它主要显示为标志名,比如git rm --cached.)

从技术上讲,暂存区中的内容是文件的 名称和模式 ,加上 冻结 de-duplicated 的引用复制。然而,你可以认为它 就好像 它持有冻结副本的可重写版本,因为 Git 如何使用它:当你 运行 git add, Git:

  • 读取文件的工作树副本;
  • 将它压缩成冻结的形式,但还没有完全冻结它;2
  • 检查是否重复;和
  • 用冻结的形式更新索引,re-using现有的副本,或者不是,视情况而定。

如果您为此时不在索引中的文件添加 新名称,Git 添加新名称和 frozen-format 内容到索引。使用 git rmgit rm --cached 也会 删除 适当的名称和引用。

所以结果是,当您第一次签出 某个提交时,索引包含该提交的完整副本3。当您 运行 git add 时,索引副本会更新。实际上,索引始终包含您的建议的下一次提交

当您实际上 运行 git commit 进行新提交时,Git 获取 frozen-format 索引内容并将其真正冻结为真正的提交。该新提交获得一个新的唯一哈希 ID,并且 Git 更新当前的 分支名称 以将新提交记住为最新提交(Git 安排新的commit 的 metadata 以记住前一个 branch-tip 提交哈希 ID)。

现在,关于索引的事情是这样的:只有一个索引。4 所以没有办法说“这个是 X 点的索引”,然后是“这是 Y 点的索引”:只有一个索引,git add 覆盖了 point-X 索引,当你使 point-Y 存在时.

所有这一切的最终结果是,因为只有一个索引,所以只有一个暂存 ara。没法命名,因为是the index.


2从技术上讲,Git 确实 添加了一个 frozen-format blob 对象 如有必要,立即到对象数据库。但是,如果它从未被使用过,它最终会被丢弃。所以它好像它从来没有被添加到这里,这就是为什么它比“冰冻”更“泥泞”。

3因为索引里的是de-duplicated,commit出来的文件肯定已经在数据库里了,所以这个copy不需要space。索引条目本身确实占用了一点 space——大致上每个文件大约 100 字节——但是文件内容都是 de-duplicated.

之前的

4这在技术上是错误的,原因有二:

  • 最重要的是,每个工作树有一个可区分的索引。使用 git worktree add 你可以创建一个新的工作树,并带有一个新的索引。
  • 为了 Git 自己的内部目的,可以创建并填充一个 临时 索引。这样做非常棘手,但是当您使用 git commit --onlygit commit --includegit commit 需要这样做。实际上,对于git commit --only,Git需要创建两个个临时索引文件。使用临时索引文件 而不是 索引的能力已导出:您可以使用脚本自己完成。但是如果这样做,您必须格外小心,因为 the 索引——这个特定工作树的区别索引——必须 保持正确的关系使用当前的提交和工作树。如果不小心维护关系,next git commit 会提交错误的文件。

因此,虽然可以有多个索引文件处于活动状态,但您不想长时间(例如几秒钟)执行此操作。当 git commit 命令创建其临时索引副本时,它们只会持续到提交本身完成。