From: D. Ben Knoble Date: Tue, 15 Sep 2026 21:16:30 GMT Subject: Re: [BUG] stash.index=true leaves a redundant stash entry after an autostash fast-forward Message-ID: In-Reply-To: Hi Eli, Since I added stash.index configuration, thought I'd take a look. I'm out of my depth, but let's persevere anyway! On Mon, Sep 7, 2026 at 12:47 AM Eli Barzilay wrote: > > Disclaimer, the following is written by an agent, but the bug is a > real problem that I have. > > > With stash.index=true, an autostash that is applied successfully is > nevertheless stored as a stash entry, and deleting MERGE_AUTOSTASH > fails. A staged change at the time of the merge is required to > trigger it. > > Reproduction (independent of the reporter's configuration): > > #!/bin/sh > set -e > export GIT_AUTHOR_NAME=A GIT_AUTHOR_EMAIL=a@b \ > GIT_COMMITTER_NAME=A GIT_COMMITTER_EMAIL=a@b > rm -rf /tmp/gitbug && mkdir /tmp/gitbug && cd /tmp/gitbug > git init -q -b main up > cd up && echo a >u && echo z >z && git add . && > git commit -qm base && cd .. > git clone -q up dn > cd up && echo more >>u && git commit -qam up2 && cd ../dn > echo staged >>z && git add z # a STAGED change is required > git fetch -q origin > git -c stash.index=true merge --ff-only --autostash origin/main > echo "--- git stash list:"; git stash list > > Actual output: > > Updating 34a5e40..84ccd9d > Created autostash: 71d4617 > Fast-forward > u | 1 + > 1 file changed, 1 insertion(+) > Applied autostash. > error: cannot lock ref 'MERGE_AUTOSTASH': unable to resolve > reference 'MERGE_AUTOSTASH' > --- git stash list: > stash@{0}: autostash > > Expected: the same without the error and with an empty stash list, as > happens with stash.index=false (the only change to the script). I can reproduce this locally. Thanks for the helpful script. For some extra tweaking, I've put a "PATH=…:$PATH" assignment at the top that prepends my local Git build's bin-wrappers, then put "GIT_DEBUGGER=$1 GIT_TRACE2=$2" in front of the merge command; that way I can debug a few things. > Analysis > -------- > > Merge keeps its autostash in the MERGE_AUTOSTASH ref > (builtin/merge.c:1675) and applies it from finish() > (builtin/merge.c:540). apply_save_autostash_ref() resolves the ref, > applies it, and then deletes it (sequencer.c:4821-4848). > > The apply is a child process, `git stash apply ` > (sequencer.c:4737-4751). stash.index turns that into an --index > apply, which takes the index-restoring branch of do_apply_stash() and > calls reset_head() (builtin/stash.c:684-691), i.e. a > `git reset --quiet --refresh` child (builtin/stash.c:455-467). > > That reset has no pathspec, so it calls remove_branch_state() > (builtin/reset.c:543) -> remove_merge_branch_state() > (branch.c:829-838), whose last statement is > > save_autostash_ref(r, "MERGE_AUTOSTASH"); > > which stores the autostash into refs/stash and deletes the ref -- in > the middle of the very apply that was about to consume it. Control > returns to apply_save_autostash_ref(), the apply reports success > ("Applied autostash."), and its refs_delete_ref() then fails on a ref > that is already gone, producing the error line. And this lines up with the code, I think. I find it a bit odd that "git stash apply --index" ends up getting to a reset mode that tries to throw away a bunch of branch state! Ideally that would be simpler, I think, but I don't see an easy way to do it with the existing "git reset" subprocess. I'm experimenting with something that swaps that out for a call to reset_working_tree(), but I don't think I've gotten it quite right for this bug yet (let alone run other test cases that might be affected by this change). BTW, it's really weird to me that the reset manual doesn't mention all these "extra" cleanups reset does via remove_merge_branch_state()! > Possible directions, in case they are useful: remove_merge_branch_state() > is about ending a merge, and `git stash apply --index` is not ending > one -- having stash's reset_head() avoid the branch-state cleanup, or > teaching an in-flight autostash apply to shield MERGE_AUTOSTASH, would > both close it. Making apply_save_autostash_ref() tolerate a missing > ref would silence the error but leave the duplicate entry. I also thought briefly about disabling stash.index for a merge autostash, but that's really papering over things, I think. I'll keep noodling on this (hopefully tomorrow morning), but in the meantime input from others welcome :) -- D. Ben Knoble