简体   繁体   English

有没有办法存储当前的 git 索引,`git add -u`,然后将索引恢复到存储的 state?

[英]Is there a way to stash the current git index, `git add -u`, and then restore the index to the stashed state?

The idea here is that I want to (temporarily) stage all pending changes, then later undo it, without ever changing files in the working tree.这里的想法是我想(暂时)暂存所有待处理的更改,然后再撤消它,而无需更改工作树中的文件。 It seems like it should be in the domain of git stash , but I can't figure out how to make it happen using that command.它似乎应该在git stash的域中,但我不知道如何使用该命令实现它。 Thanks!谢谢!

Edit: regarding your comment :编辑:关于您的评论

@phd sure, that's how I can grab an initial snapshot of the working copy+index state. @phd 当然,这就是我如何获取工作副本+索引 state 的初始快照的方法。 But the challenge is how to roll back to that state after git add -u without changing any files in the working copy .但挑战是如何在git add -u之后回滚到 state而不更改工作副本中的任何文件

For reference, the @phd here is looking at an answer involving a git stash store of the result of a git stash create .作为参考,这里的@phd正在查看涉及git stash create结果的git stash store的答案。 The git stash create operation first creates two trees: one is the result of git write-tree (as outlined below), and the other is in effect the result of copying the index to a temporary index, running git add -u , and then running git write-tree again, all using the temporary index. git stash create操作首先创建两棵树:一棵是git write-tree的结果(如下所述),另一棵实际上是将索引复制到临时索引的结果,运行git add -u和再次运行git write-tree ,全部使用临时索引。 These two trees—if they're written at all;这两棵树——如果有的话; git stash create does nothing when your index and working tree are both "clean"—are then wrapped up into the I and W commits described in the git stash documentation , and git stash create prints the hash ID of the W commit (suitable for use with git stash store ). git stash create does nothing when your index and working tree are both "clean"—are then wrapped up into the I and W commits described in the git stash documentation , and git stash create prints the hash ID of the W commit (suitable for use与git stash store )。

Note that your working tree is not disturbed by this part of the operation.请注意,您的工作树不会受到这部分操作的干扰。 There is nothing to restore.没有什么可以恢复的。 What disturbs your working tree is the git reset step of git stash push .打扰您的工作树的是git stash pushgit reset步骤。 But git stash create does not run this step!但是git stash create没有运行这一步! It just makes the I and W commits.它只是进行IW提交。 So if that's all you want, git stash create will get you what you want.因此,如果这就是您想要的, git stash create将为您提供您想要的。

my project has a pre-commit hook that lints only the staged file states...我的项目有一个预提交钩子,它只检查暂存文件状态......

In this case, why not:在这种情况下,为什么不:

  • make the W commit (use git stash create if you like), then进行W提交(如果您愿意,可以使用git stash create ),然后
  • optionally, give that a temporary branch name just for sanity purposes, then可选地,仅出于理智目的给它一个临时分支名称,然后
  • use git worktree add to check out that branch, or that commit as a detached-HEAD, then使用git worktree add来签出该分支,或者将该提交作为分离头,然后
  • run the pre-commit hook over the added work-tree.在添加的工作树上运行预提交挂钩。

If the linter is really well behaved, you could perhaps skip the added worktree entirely and just set GIT_INDEX_FILE to a temporary file into which you read the tree from the W commit, and run the linter with that GIT_INDEX_FILE setting in place.如果 linter 表现得非常好,您也许可以完全跳过添加的工作树,只需将GIT_INDEX_FILE设置为您从W提交中读取树的临时文件,然后使用该GIT_INDEX_FILE设置运行 linter。

[Edit 2:] If the linter is really well behaved—few of them seem to be this well-behaved—you should be able to just set GIT_INDEX_FILE to a temporary file, run git add -u , run the linter, and then remove the temporary file. [编辑 2:] 如果 linter 表现得非常好——似乎很少有人表现得这么好——你应该能够将GIT_INDEX_FILE设置为一个临时文件,运行git add -u ,运行 linter,然后删除临时文件。 Of course it might be even easier to run the linter over the working tree, rather than over an index.当然,在工作树上运行 linter 可能比在索引上更容易。 That requires a pre-commit linter that is flexible: it can either use a working tree (ignoring the existence of Git entirely) in the style of older, dumber, non-Git-aware linters;这需要一个灵活的预提交 linter:它既可以使用工作树(完全忽略 Git 的存在),也可以采用更旧、更笨、不支持 Git 的 linter; or use a Git index.使用 Git 索引。 This would be a lot faster and easier overall.总体而言,这将更快,更容易。

[original answer below line] [以下行的原始答案]


I think we have an XY problem here, but just to answer the question as asked:我认为我们这里有一个XY 问题,但只是为了回答所问的问题:

  • there's no point as there is only one index;没有意义,因为只有一个索引; but
  • you can temporarily use a temporary index;您可以临时使用临时索引; and
  • you can literally do that, it's just pointless.您可以从字面上做到这一点,这毫无意义。

The way Git uses the index is complicated, so there are many rough edges here. Git使用索引的方式比较复杂,所以这里有很多毛边。 You could cut yourself on any of them.你可以在其中任何一个上割伤自己。 For instance, the index could have assume-unchanged and/or skip-worktree bits set on particular entries, or could be in the expanded state that Git uses in the middle of a conflicted merge (including cherry-pick, revert, and rebase operations).例如,索引可能在特定条目上设置了假设未更改和/或跳过工作树位,或者可能位于 Git 在冲突合并(包括cherry-pick、revert 和 rebase 操作)中间使用的扩展 state )。 But in general, the index can be turned into one of Git's internal tree objects at almost any time using git write-tree :但总的来说,索引几乎可以随时使用git write-tree转换为 Git 的内部树对象之一:

hash=$(git write-tree) || echo "unable to turn index into a tree"

This write-tree operation fails if the index is in the expanded, unmerged state;如果索引在扩展的、未合并的 state 中,则此写入树操作将失败; otherwise, it succeeds, even if there are some flag bits set.否则,即使设置了一些标志位,它也会成功。 If it succeeds, hash now contains the hash ID of a tree object.如果成功, hash现在包含树 object 的 hash ID。 This tree object will live on in the repository for at least 14 days by default, so you have up to 14 days to create a commit that uses it, or otherwise link to it with a tag or some other reference, which will keep it alive more permanently.默认情况下,这棵树 object 将在存储库中至少存在 14 天,因此您有长达 14 天的时间来创建使用它的提交,或者使用标签或其他引用链接到它,这将使其保持活动状态更永久。

Having written out the index as a tree, you can now do anything you like to the index, including running git add -u .索引写成树后,您现在可以对索引执行任何操作,包括运行git add -u Later, you can restore the index to the state it has in that saved tree using git read-tree :稍后,您可以使用git read-tree将索引恢复到它在保存的树中的 state :

git read-tree $hash || echo "unable to restore index"

The read-tree operation can fail in various cases;读树操作在各种情况下都可能失败; consult the documentation .查阅文档 Some flags make some operations succeed or fail when they might otherwise fail or succeed respectively, and you can also completely empty the index first, then read into it, for instance.一些标志分别使某些操作成功或失败,否则它们可能会失败或成功,例如,您也可以先完全清空索引,然后再读入它。

To use a temporary index, set the environment variable GIT_INDEX_FILE to the name of the temporary index file.要使用临时索引,请将环境变量GIT_INDEX_FILE设置为临时索引文件的名称。 This file must either not exist at all, or be a valid index file.此文件必须根本不存在,或者是有效的索引文件。 An empty file (as made by mktemp for instance) is not suitable.一个空文件(例如由mktemp创建的)是不合适的。 You can, however, use:但是,您可以使用:

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

in sh or bash scripts.在 sh 或 bash 脚本中。 (Use a similar construct in whatever language you like.) Operations will now create an index and fill it in, using the temporary file, so you can now fiddle with that, without affecting the real index. (使用您喜欢的任何语言的类似结构。)操作现在将创建一个索引并使用临时文件填充它,因此您现在可以摆弄它,而不会影响真正的索引。 Run Git commands with GIT_INDEX_FILE unset to use the real index, and with GIT_INDEX_FILE=$TF to use the temporary index:运行 Git 命令,未设置GIT_INDEX_FILE以使用实际索引,并使用GIT_INDEX_FILE=$TF使用临时索引:

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

for instance will demonstrate the effect.例如将演示效果。 The last line uses a subshell so as to unset the environment variable for the duration of the final git ls-files , but retain the environment variable in the mainline execution, for whatever commands come after the final comment.最后一行使用一个子shell,以便在最终git ls-files的持续时间内取消设置环境变量,但在主线执行中保留环境变量,以用于最终注释之后的任何命令。

As always, temporary objects created for whatever purpose—including any blobs you might put in the temporary index—have that 14-day grace period to turn into real commits.与往常一样,出于任何目的而创建的临时对象(包括您可能放入临时索引中的任何 blob)都有 14 天的宽限期以转换为真正的提交。 After that, git gc might remove them, even if you are not yet done with them.之后, git gc可能会删除它们,即使您尚未完成它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM