有没有一种方法可以重新排序Git的暂存区?

14

在Git中,一个人可以创建多个储藏:

git stash save "quick temp stash"
git stash save "another quick temp stash"
git stash save "This is important but I need to put it on the back burner"
git stash save "This is almost certainly garbage, but just in case ..."

现在,我知道我可以以任何顺序获取那些隐藏的物品:

git stash pop stash@{3} # recover quick temp stash
git stash pop stash@{2} # recover another quick temp stash

但是显然更方便的语法更可取:

git stash pop

这不仅是因为要输入的内容更少,而且需要思考的内容也更少:如果我有几个藏匿物,我必须查找它们,可能会使用git stash show命令查看其中一些,直到找到我想要的那个。但如果我只保留最近的“temp”藏匿物在最上面(其次是下一个temp藏匿物等等),我根本不需要思考;我只需要使用pop命令即可。

所以,我的问题是,是否有任何方法可以重新排序我的藏匿物,以便我可以在保存它们时考虑何时应该“pop”,而不是在“pop”它们时考虑。这将使我仍然能够保存“这几乎肯定是垃圾,但以防万一…”和“这很重要,但我需要把它放在后面”的藏匿物,但它们位于藏匿列表的后面,不会干扰访问更简单的快速藏匿物。


我知道我可以一个个弹出每个存储区,依次提交,然后在所需的顺序重新设置这些提交,然后重置(软重置)每个提交并将其转换回存储区。但不仅很麻烦,而且如果存储区来自不同的分支,可能甚至无法正常工作,因此我希望有其他方法。 - machineghost
3个回答

6

Whymarrh's answerDonnie's comment所述,我认为你最好只是提交。不过,需要注意的是:

如果你真的想继续使用藏匿并重新排序,可以在工作时调整refs/stash...

这可以在完全不使用git stash pop的情况下完成。只是有点棘手。(编辑:仔细阅读后我发现这是Whymarrh的答案中的想法。)

reflog文件.git/logs/refs/stash保存了从1到N(存在多少个就有多少个)的reflog条目。而stash参考本身则保存了条目零。

drop操作包括删除特定的reflog条目(git reflog delete知道如何处理特殊的零情况):

drop_stash () {
        assert_stash_ref "$@"

        git reflog delete --updateref --rewrite "${REV}" &&
                say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
                die "$(eval_gettext "\${REV}: Could not drop stash entry")"

        # clear_stash if we just dropped the last stash entry
        git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
        clear_stash
}

(其中clear_stash删除refs/stash本身)。$REV参数是refs/stash@{N},如果您没有指定特定的参数,则为refs/stashstore操作使用git update-ref在零处插入条目:
[snip]
        w_commit="$1"
        if test -z "$stash_msg"
        then
                stash_msg="Created via \"git stash store\"."
        fi

        git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
        ret=$?
        test $ret != 0 && test -z "$quiet" &&
        die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
        return $ret

因此,实现“滚动”操作是可能的,尽管有点棘手(如果您熟悉Forth或Postscript)。例如,将底部三个条目向上滚动一步,更改为:
E  stash@{4}
D  stash@{3}
C  stash@{2}
B  stash@{1}
A  stash@{0}

转化为:

E  stash@{4}
D  stash@{3}
B  stash@{2}
A  stash@{1}
C  stash@{0}

如果你想把C复制到底部,就像通过一个新的store一样,然后删除stash@{3}(由于在零处插入而上移)。

实际上,你可以通过运行git stash -q storegit stash -q drop来完成这个操作,使用适当的参数。


1
需要一些时间重新阅读并编写一个适当的脚本(希望是一个漂亮简单的),但那看起来就是我正在寻找的。谢谢! - machineghost
有趣的是,使用备受厌恶的Mercurial MQ,这是一个微不足道的任务(就像使用“quilt”一样)。我最好倾倒git stash list -p并拆分补丁,然后重新应用它们。 - gavenkoa
1
@gavenkoa:反正我不喜欢git stash。如果我有一个或两个以上的存储,那么我肯定做错了什么,应该将它们大部分或全部转换为Git分支。 - torek
@torek 这是真的。需要忘记 stash 并开始使用临时分支。stash 隐藏了一些操作,比如不需要提交消息和不需要分开的 checkout/rebase。但它不是一个适合长时间推迟工作的正确工具。而且 stash 的堆叠是噩梦)) - gavenkoa

3

不能直接使用——我认为stash并不适合这种工作流程。

话虽如此,如果您切换到临时提交,您可以按照自己的意愿重新排序这些提交并挑选提交(例如,git cherry-pick $tempbranch 代替 git stash pop)。

如果您真的想继续使用stash并重新排序它们,您可以在工作时调整refs/stash(通过一些脚本)。从git-stash文档中了解:

您创建的最新暂存被存储在refs/stash中;旧的暂存可以在此引用的reflog中找到,并且可以使用通常的reflog语法进行命名(例如,stash@{0}是最近创建的暂存,stash@{1}是它之前的一个,stash@{2.hours.ago}也是可能的)。暂存也可以通过仅指定暂存索引来引用(例如,整数n等同于stash@{n})。
如果您决定为您的工作流编写脚本,您还可以使用git stash creategit stash store

create

创建一个贮藏(stash)对象(它是一个普通的提交对象),并返回其对象名称,而不将其存储在引用命名空间中。这旨在为脚本提供帮助。这可能不是您想要使用的命令;请参见上面的“save”。

store

将通过git stash create创建的给定贮藏对象(它是一个悬挂的合并提交)存储在贮藏引用中,并更新贮藏引用日志。这旨在为脚本提供帮助。这可能不是您想要使用的命令;请参见上面的“save”。


“create”/“store”很有趣,但是“store”也不能让你设置存储的索引,所以它看起来和“save”一样好或者不好……除非我漏掉了什么? - machineghost
我在脚本中提供了“create”/“store”作为选项,但在命令行下它们并没有太多用处。我已经编辑了答案,使其更加清晰明了。 - Whymarrh
我理解你的意图是用它们来编写脚本,我很乐意编写脚本,但我不明白的是,即使从脚本中调用它们,它们如何帮助解决问题。即使是脚本也似乎有同样的最终限制,那就是您需要弹出每个stash才能将它们中的任何一个“移到后面”,因此如果没有方便的方法来做到这一点(或者没有一种将stash移动到后面的方法),则似乎不可能通过脚本重新排序stash。 - machineghost

1
因此,虽然有点棘手,但可以实现“滚动”操作(如果您熟悉Forth或Postscript)。要将底部的三个条目向上滚动一步,只需将C复制到底部,就像通过新存储一样,然后删除stash@{3}(因为在零处插入而向上移动)。

如果您这样做,请确保使用Git 2.36(2022年第二季度),因为如果您要滚出大量提交,则速度太慢了。

"git stash drop"(man)被重新实现为内部调用reflog_delete()函数,而不是通过run_command() API调用 "git reflog delete"(man)

因此,所有这些stash drop调用都保留在同一个进程中,使用内部函数,而不是每个stash drop => reflog delete都需要子shell。
查看 提交 758b4d2, 提交 7d3d226, 提交 76bccbc (2022年3月2日) 由 John Cai (john-cai) 完成。
(由 Junio C Hamano -- gitster -- 合并至 提交 a2fc9c3, 2022年3月16日)reflog:将删除 reflog 功能和辅助函数进行库化” 协助者:Ævar Arnfjörð Bjarmason 签名作者:John Cai 目前,为了删除引用,stash需要将shell输出到reflog。
为了减少我们向子进程外壳输出的量,将stash所需的功能转换为reflog.c库。
添加一个reflog_delete函数,它基本上是while循环中的逻辑builtin/reflog.c cmd_reflog_delete()
这是一个builtin/reflog.cbuiltin/stash.c都可以调用的函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接