git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [BUG] stash.index=true leaves a redundant stash entry after an autostash fast-forward

From
D. Ben Knoble <ben.knoble@gmail.com>
Date
Sep 15, 2026, 21:16 UTC
Message-ID
<CALnO6CCkq7mjBUKxOYcwKX8=SrH441FuWopoGZutPk99JRTGUA@mail.gmail.com>
In-Reply-To
<CALO-guvbk2TcrVwzdNQ3yRpzHr0HHZ3h1wite0Xp0sUyAT4otA@mail.gmail.com>
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 <eli@barzilay.org> wrote:
Show 42 quoted lines
>
> 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.

Show 25 quoted lines
> 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 <oid>`
> (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()!

Show 6 quoted lines
> 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
Previous: Eli BarzilayNext: Phillip Wood
Message 2 of 12 in “[BUG] stash.index=true leaves a redundant stash entry after an autostash fast-forward”
  1. Eli BarzilaySep 7, 2026
  2. D. Ben KnobleSep 15, 2026
  3. Phillip WoodSep 16, 2026
  4. Ben KnobleSep 16, 2026
  5. Eli BarzilaySep 16, 2026
  6. D. Ben KnobleSep 17, 2026
  7. Phillip WoodSep 17, 2026
  8. D. Ben KnobleSep 17, 2026
  9. Phillip WoodSep 17, 2026
  10. Phillip WoodSep 19, 2026
  11. D. Ben KnobleSep 19, 2026
  12. D. Ben KnobleSep 19, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.