有没有办法重新订购 Git 个藏品?

Is there a way to re-order Git stashes?

在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" 存储在顶部(然后是下一个临时存储,等等),我根本不需要考虑;我只是 pop.

所以,我的问题是,有什么方法可以重新排序我的藏品,这样我就可以在我保存它们的时候考虑它们应该在什么时候 "popped",而不是在那个时候我"pop"他们。这将允许我仍然保存 "This is almost certainly garbage, but just in case ..." 和 "This is important but I need to put it on the back burner" 存储,但在存储列表的后面,它们不会使访问更简单的快速存储变得复杂。

并非开箱即用——我不认为 stash 真的适用于该工作流程。

就是说,如果您切换到临时提交,您可以按照您认为合适的方式重新排序这些提交和 cherry-pick 提交(例如 git cherry-pick $tempbranch 作为 git stash pop 的替代)。

如果您真的想继续使用存储并重新排序,您可以在工作时调整 refs/stash(通过一些脚本)。来自 git-stash documentation:

The latest stash you created is stored in refs/stash; older stashes are found in the reflog of this reference and can be named using the usual reflog syntax (e.g. stash@{0} is the most recently created stash, stash@{1} is the one before it, stash@{2.hours.ago} is also possible). Stashes may also be referenced by specifying just the stash index (e.g. the integer n is equivalent to stash@{n}).

如果您决定为您的工作流程编写脚本,您还可以使用 git stash creategit stash store

create

Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the ref namespace. This is intended to be useful for scripts. It is probably not the command you want to use; see "save" above.

store

Store a given stash created via git stash create (which is a dangling merge commit) in the stash ref, updating the stash reflog. This is intended to be useful for scripts. It is probably not the command you want to use; see "save" above.

and 一样,我认为您可能通过承诺获得更好的服务。不过,我会注意到:

If you really wanted to keep using stashes and reorder those, you could adjust refs/stash as you work ...

完全不用 git stash pop 也可以做到这一点。这很棘手。 (编辑:我在重新阅读时看到这是 Whymarrh 的回答中的想法。)

reflog 文件,.git/logs/refs/stash,包含 reflog 条目 1 到 N(无论存在多少)。 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@{<em>N</em>},如果您未指定,则为 refs/stash一个特别的。

store 操作在零处插入条目,使用 git update-ref:

[snip]
        w_commit=""
        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

因此有可能实现 "roll" 操作(如果您熟悉 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 以及适当的参数来做到这一点。

It's therefore possible, albeit a bit tricky, to achieve a "roll" operation (if you're familiar with Forth or Postscript).
To roll the bottom three entries up one step, you would just copy C to the bottom, as if via a new store, then drop stash@{3} (which moved up because of the insertion at zero).

如果您这样做,请确保使用 Git 2.36(2022 年第 2 季度),因为如果您要推出大量提交,它会太慢。

"git stash drop"(man) is reimplemented as an internal call to reflog_delete() function, instead of invoking "git reflog delete"(man)通过 run_command() API.

因此,不是每个 stash drop => reflog delete 的子 shell,所有这些 stash drop 调用都保留在同一进程中,使用内部函数。

参见 commit 758b4d2, commit 7d3d226, commit 76bccbc (02 Mar 2022) by John Cai (john-cai)
(由 Junio C Hamano -- gitster -- in commit a2fc9c3 合并,2022 年 3 月 16 日)

reflog: libify delete reflog function and helpers

Helped-by: Ævar Arnfjörð Bjarmason
Signed-off-by: John Cai

Currently stash shells out to reflog in order to delete refs.
In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete().
This is a function that builtin/reflog.c and builtin/stash.c can both call.