【问题标题】:Is there a limited lifetime for the staging area in Git?Git 中的暂存区是否有有限的生命周期?
【发布时间】:2022-01-23 06:04:28
【问题描述】:

我刚刚开始学习 Git。目前我所知道的是,在提交到主存储库之前,“快照”会被添加到暂存区域。问题是,我是否应该关心这些“快照”可以在暂存区保留多长时间?当我关闭电脑时,所有数据都会被删除吗?

如果暂存区内容没有生命周期,那么它与存储库本身有何不同?在这种情况下,在我看来,它就像是第二个存储库。

【问题讨论】:

标签: git staging


【解决方案1】:

Timothy Truckle's answer 是正确的,但我会补充几点:

  • git worktree add 不仅会创建一个新的工作树,而且还会创建一个新的索引/暂存区,该索引/暂存区是该新工作树的私有。
  • 在 Git 版本 2.5 到 2.14 中存在一个错误 - 一个相当糟糕的错误,已在 2.15 中修复,与此相关。

错误是这些添加的工作树中的索引/暂存区确实有一个有限的生命周期,这是错误的。存储在其中的某些文件可能会在 14 天后被错误地销毁。因此,如果您有 2.15 之前的 Git,并使用 git worktree add,请在两周内在这个添加的工作树中完成您的工作。 (或者更新到更正的 Git:更新足以保持其添加的索引文件完好无损;您无需对添加的工作树执行任何操作;您只需要在暂存区被破坏之前进行升级。)

可选的附加阅读

如果暂存区内容没有生命周期,那么它与存储库本身有何不同?在这种情况下,在我看来,它就像是第二个存储库。

Git 存储库大致上是一个包含提交 的大型数据库。这些是编号的(按大的、丑陋的、看起来随机的哈希 ID),Git 按该编号将它们从数据库中拉出。因此,在 Git 找到提交之前,您必须提供任何提交 Git 的哈希 ID。我们有分支和其他名称,因此我们,仅仅是人类,可以使用辅助数据库(将名称转换为数字)来帮助我们(和 Git)找到提交,而无需记住看起来随机的哈希 ID。

indexstaging area,或者(用最古老和最糟糕的术语)cache,在 Git 中实际上并不是一个犯罪。它有一小部分杂项工作,因此没有一个完美的描述,但 staging area 之所以是一个很好的名字,是因为它在所有时间都拥有一个 建议下一次提交

(有时——在冲突合并期间——它会变得一团糟,实际上无法提交。但即便如此,它也会保留某种提议的下一次提交。只是提议太多在其中,包括无法提交的额外内容。在这种情况下,git status 将告诉您有关 unmerged 文件的信息。但是如果我们忽略这种特殊情况,并忽略索引的其他额外角色播放——例如加快 git status 处理 untracked 文件的速度,例如可选的“untracked cache”——“proposed next commit”描述非常适合。)

由于 repository 是一个提交数据库,而 index / staging-area 只是一个 单个提议的提交,但实际上尚未提交,因此两者之间存在很大差异索引和存储库。事实上只有一个索引——或者更准确地说,每个添加的工作树一个,加上一个原始的1——意味着你在这里最多只能持有一个额外的提交(或者 N+1 N 是添加的工作树的数量)。

也值得看看git status 在这里的工作原理:

  • 有一个当前提交,可以使用名称HEAD 找到。与所有提交一样,此提交是只读的:它实际上无法更改。 (您可以切换到其他一些当前提交,即更改哪个提交是当前的,但您不能更改任何提交的内容。)

  • 然后在索引/暂存区域中有提议的下一次提交。你可以改变这里的内容。它们以预先 Git 化的格式存储,准备好提交:一旦文件位于索引/暂存区域中,文件就已经被删除重复和 Git 化。

  • 最后,在您的工作树中,有一些您正在处理/使用的文件。您也可以更改这些,因为这些只是普通文件。 您计算机上的任何程序都可以读取或写入这些,而不仅仅是 Git。

所以当您运行git status 时,它会进行两个 diff,而不仅仅是一个。第一个差异将HEAD 中的文件与索引/暂存区域中的文件进行比较。 对于每个相同的文件,Git 什么都不说。对于任何不同的文件,Git 都会说staged for commit

运行该差异后,git status 还会运行 second 差异,以将索引中的文件与工作树中的文件进行比较。再一次,对于 相同 的文件,Git 什么也没说。 对于任何不同的文件,Git 都会说not staged for commit

当您有一个大型项目时,这种阴暗的“隐藏文件”舞蹈很有用的原因就很明显了。假设当前提交中有 10,000 个文件。因此,索引中有 10,000 个文件(可能加上或减去一个或两个),而索引中有 10,000 个文件(加上或减去几个,加上可能是一堆您永远不会提交的 untracked 文件)工作树。如果git status 每次都报告(两次!)所有 10k 个文件,您将如何在所有噪音中找到有用的数据(例如,您更改了其中的三个)?您想要一个有用的摘要,而不是包含三个有用名称的 9997 个未修改文件的列表。

(如果你想要原始列表,git ls-files --stage 会得到它。)


1这个有点笨拙的措辞(为什么不只是“工作树的数量”?)的原因是 Git 有所谓的 bare 存储库,它没有工作树。这些存储库仍然有一个索引。因此,在一个裸仓库中,我们有一个暂存区,但工作树为零。添加三个工作树,我们现在有四个暂存区和三个工作树。

我认为在 any 存储库中拥有初始工作树和索引/暂存区从根本上说是错误的,并且所有存储库都应该 start "裸”,然后添加工作树,默认情况下可能是一个,但仍作为附加组件。因此,一个真正的裸仓库将没有索引,并且 所有 工作树将被“添加”,而不是在非裸仓库的“主要工作树”和所有添加之间有一个奇怪的区别-在那些。但这已经太迟了。对于大多数用户来说,这将是一个没有区别的区别,但它会解决一些棘手的内部问题,其中一个问题目前正在邮件列表中引起大量讨论。

【讨论】:

    【解决方案2】:

    请记住,只有一个暂存区。 这意味着,当您切换分支时,暂存区的内容将被带到另一个分支。 当目标分支与暂存区发生冲突时,Git 甚至会拒绝切换(除非你给了强制切换 -f),就像本地更改冲突时一样。

    使用暂存区准备干净的提交clean commit 是指您的项目编译所有自动化(单元)测试通过。

    有时您可能会在代码库未编译的情况下进行更大的更改,但您的某些工作应该得到保护。
    尤其是在进行测试驱动开发时,您经常会遇到相反的情况:您已经完成了一个微循环(代码库编译并且单元测试通过),但是您当前的单元测试还没有完成。

    在这两种情况下,您都暂存当前状态 (git add)。

    一旦您的任务完成(项目编译和所有测试通过),您将最新的本地更改添加到阶段并提交暂存状态。

    是的,这样你会得到很多相当小的提交。 但这是一件好事:

    • 小提交需要更少的词来描述它们 -> 提交信息的第一行通常就足够了。这样git log -oneline 会显示您所做的文档。

    • 小提交在rebase 上发生冲突的可能性较小

    • 小提交可以更容易地找到文件中的某个更改,因为更改的文件并不多,提交消息可以更好地提示哪个提交可能有该更改。

    • 小提交使得通过 cherry-picking 将更改从一个分支转移到另一个分支变得更加容易。

    毕竟,如果需要,您仍然可以稍后压缩它们以进行一次提交。

    【讨论】:

    • 可以将正在进行的工作提交到您的功能分支。它可以在以后被压缩,并有助于在计算机之间移动您的工作。你不能推动暂存区。
    • @MadPhysicist 这取决于您认为正在进行的工作。我个人不会签入在其他人签出时可能导致麻烦的东西,尤其是不可编译状态或自动化测试失败。穿过你的心::你是否总是记得在推之前有一个壁球要做?
    • @TimothyTruckle 我想这取决于“签到”的含义。在我的组织中,“签入”意味着将 PR 完成到共享分支中。在你自己的分支上推送提交只是从你自己的机器上“保存你的工作”。
    • @TimothyTruckle。 TTT 几乎涵盖了它。我的“保存的工作”被标记为“WIP”,我们通常知道不要期望它通过测试,有时甚至不会构建。我们通常在合并到共享分支时压缩整个功能。
    • @TTT 我的观点是:当我的功能分支的所有提交都是“干净的”时,我可以完美地检查在 rebase 冲突解决期间是否成功:因为目标分支和冲突commit where clean(编译所有测试通过)冲突解决后必须再次清除冲突的提交。它更多的是为了减轻我的工作......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 2016-08-14
    • 1970-01-01
    相关资源
    最近更新 更多