有没有办法将当前的 git 索引,`git add -u` 隐藏起来,然后将索引恢复到隐藏状态?

问题描述

这里的想法是我想(暂时)暂存所有挂起的更改,然后再撤消它,而无需更改工作树中的文件。似乎它应该在 git stash 的域中,但我不知道如何使用该命令实现它。谢谢!

解决方法

编辑:关于your comment

@phd 当然,这就是我如何获取工作副本+索引状态的初始快照。但挑战在于如何在git add -u 不更改工作副本中的任何文件之后回滚到该状态。

作为参考,此处的 @phd 正在查看涉及 git stash store 结果的 git stash create 的答案。 git stash create 操作首先创建两棵树:一个是 git write-tree 的结果(如下所述),另一个是实际上将索引复制到临时目录的结果索引,运行 git add -u,然后再次运行 git write-tree,全部使用临时索引。这两棵树——如果它们有写的话;当您的索引和工作树都“干净”时,git stash create 什么也不做——然后被包装到 the git stash documentation 和 {{1} 中描述的 IW 提交中} 打印 git stash create 提交的哈希 ID(适用于 W)。

请注意,您的工作树不会受到这一部分操作的干扰。没有什么可以恢复的。扰乱您的工作树的是git stash storegit reset 步骤。但是 git stash push 不运行这一步!它只是进行 git stash createI 提交。因此,如果这就是您想要的,W 将为您提供所需的一切。

我的项目有一个预提交钩子,它只检查暂存文件状态......

在这种情况下,为什么不:

  • 进行 git stash create 提交(如果您愿意,可以使用 W),然后
  • 或者,出于理智考虑,给它一个临时分支名称,然后
  • 使用 git stash create 来检查那个分支,或者作为一个分离的 HEAD 提交,然后
  • 在添加的工作树上运行预提交挂钩。

如果 linter 的行为真的很好,你也许可以完全跳过添加的工作树,只需将 git worktree add 设置为一个临时文件,你从 GIT_INDEX_FILE 提交中读取树,然后运行 ​​linter使用 W 设置。

[编辑 2:] 如果 linter 真的表现良好——其中很少有人表现得这么好——你应该能够将 GIT_INDEX_FILE 设置为一个临时文件,运行 GIT_INDEX_FILE,运行 linter,然后删除临时文件。当然,在工作树上运行 linter 可能更容易,而不是在索引上运行。这需要一个灵活的预提交 linter:它可以使用更老、更笨、不支持 Git 的 linter 风格的工作树(完全忽略 Git 的存在); 使用 Git 索引。总的来说,这会更快、更容易。

[原答案下一行]


我认为我们在这里有一个 XY problem,但只是为了回答这个问题:

  • 没有意义,因为只有一个索引;但是
  • 可以临时使用临时索引;和
  • 你真的可以这样做,只是毫无意义。

Git 使用索引的方式比较复杂,所以这里有很多毛边。你可以在其中任何一个上割伤自己。例如,索引可能在特定条目上设置了假定未更改和/或跳过工作树位,或者可能处于 Git 在冲突合并中间使用的扩展状态(包括cherry-pick、revert 和 rebase 操作) )。但总的来说,索引几乎可以随时使用 git add -u:

变成 Git 的内部树对象之一
git write-tree

如果索引处于展开、未合并状态,则写树操作失败;否则,即使设置了一些标志位,它也会成功。如果成功,hash=$(git write-tree) || echo "unable to turn index into a tree" 现在包含树对象的哈希 ID。默认情况下,此树对象将在存储库中保留至少 14 天,因此您最多有 14 天的时间创建使用它的提交,或者以其他方式使用标签或其他引用链接到它,这将使其保持活动状态更永久。

将索引写出作为一棵树,您现在可以对索引执行任何您喜欢的操作,包括运行hash。稍后,您可以使用 git add -u:

将索引恢复到它在保存的树中的状态
git read-tree

读取树操作可能在各种情况下失败;咨询the documentation。一些标志使某些操作成功或失败,而它们可能分别失败或成功,例如,您也可以先完全清空索引,然后再读入索引。

要使用临时索引,请将环境变量 git read-tree $hash || echo "unable to restore index" 设置为临时索引文件的名称。这个文件要么根本不存在,要么是一个有效的索引文件。空文件(例如由 GIT_INDEX_FILE 制作)是不合适的。但是,您可以使用:

mktemp

在 sh 或 bash 脚本中。 (在您喜欢的任何语言中使用类似的构造。)操作现在将创建一个索引并使用临时文件填充它,因此您现在可以摆弄它,而不会影响真正的索引。运行 Git 命令,未设置 TF=$(mktemp) || exit rm -f $TF # remove it now trap "rm -f $TF" 0 1 2 3 15 # arrange to remove it on exit or signal export GIT_INDEX_FILE=$TF # point Git at this name from here on 以使用实际索引,并使用 GIT_INDEX_FILE 使用临时索引:

GIT_INDEX_FILE=$TF

例如将演示效果。最后一行使用子shell,以便在最后的 git read-tree --empty # create empty $TF index file echo "temp index contents:" git ls-files --stage echo "real index contents:" (unset GIT_INDEX_FILE; git ls-files --stage) # back to using the temporary index in $TF 期间取消环境变量的设置,但在主线执行中保留环境变量,无论是在最终注释之后的任何命令。

与往常一样,为任何目的创建的临时对象(包括您可能放入临时索引的任何 blob)都有 14 天的宽限期来转变为真正的提交。之后,git ls-files 可能会删除它们,即使您还没有完成它们。