Uh oh!
There was an error while loading. Please reload this page.
fix: survive stashing untracked files from inside the current directory - #3015
fix: survive stashing untracked files from inside the current directory#3015VXNCXNX wants to merge 1 commit into
Conversation
The startup path was stored as a literal "." and reopened on every operation, so stashing with untracked files included deleted the cwd and every later call failed to resolve it. Resolve to the repo workdir root once at startup instead.
CAOShurong
left a comment
There was a problem hiding this comment.
I independently validated exact head 5c3d858a: cargo fmt -- --check passed; cargo nextest run -p asyncgit stash passed 14/14; full asyncgit nextest passed 173/173; and a separate pygit2 reproduction showed the baseline stored subdirectory becomes unreopenable after libgit2 stashes/removes it, while resolving to the workdir root keeps the stash reopenable. The fix itself looks correct for the git2 path.
One follow-up: the PR description says opening the Log tab in the same post-stash scenario still panics through gix_repo (CurrentDir(NotFound)). If that remains reproducible on this head, could you please file/link a separate issue (or narrow the PR's scope explicitly)? I don't think it invalidates this focused git2 fix, but linking it would make the remaining user-visible limitation actionable.
AI disclosure: this review was prepared with agent assistance; I independently ran the commands above and checked the exact head.
VXNCXNX
commented
Aug 15, 2026
Thanks for running it independently, and for the disclosure. Filed as #3017. It does still reproduce on this head. I re-checked it two ways before filing: with an explicit absolute repo root, and with The reason it cannot be fixed from the path side is that gix reads the cwd before it looks at the path it was handed: Two things I did not have in the PR description and put in the issue: the panic aborts the whole process through rayon rather than just the log thread, and the filtered log path does not hit it, since that one goes through git2. So it only fires with no commit filter active. Happy to narrow this PR's description if you would rather it not mention the gix bug at all now that it has its own issue. |
This Pull Request fixes/closes#2651.
It changes the following:
RepoPath::Pathis resolved once at startup to the repository workdir root, so stashing untracked files from inside an untracked subdirectory no longer leaves every later operation reopening a deleted inode.--filefrom a subdirectory now produces a path relative to the repo root rather than to the cwd. See the note below, this overlaps fix: resolve --file from repository subdirectories #2989.What's broken
Stashing with untracked files included, while the cwd is an untracked subdirectory, deletes that directory and then every later operation fails:
Worth noting that
git stash -urefuses this outright, printing "Refusing to remove current working directory" and keeping the directory. libgit2 has no such guard, which is why gitui hits it and the CLI does not.The cause
DEFAULT_GIT_DIRis the literal".", and that string is what lands inRepoPath::Path.repository::repo()reopens from it on every operation.stash_savepassesINCLUDE_UNTRACKEDtostash_save2, which removes the now-empty untracked directory that is the process cwd, so the next reopen resolves"."against a deleted inode.The fix
Resolve the path once at startup to the repository workdir root, and store that.
Canonicalizing the cwd is not enough, which I checked before writing this: the canonical path is the very directory that gets deleted, so it fails identically. Only the workdir root survives.
RepoPath::Workdir(from--workdir/--directory) is returned untouched. Bare repos fall back torepo.path(), sinceworkdir()isNonethere.What this does to --file, since it overlaps #2989
Please look at this part specifically.
app.rsrelativizes the selected file by strippinggitpath()from it, so making the root absolute changes what that produces from a subdirectory:The old value was relative to the cwd rather than to the repo, which is not a valid git path, and is why
--filecurrently does nothing from a subdirectory. So this incidentally fixes that too, which is the same ground #2989 is on. If you would rather keep the two separate I can gate the resolution so--filebehaviour is untouched here, just say which you prefer.start_terminalalso canonicalizes the path for the window title. That becomes a no-op, since the path is already an absolute existing directory.Verification
cargo fmt -- --checkclean,cargo clippy --workspaceclean, and the asyncgit suite is 178 passed, 0 failed.The new test creates an untracked subdirectory, stashes with untracked included, and asserts a later
get_stashesstill works. Reverting the resolution with the test kept reproduces the issue's error exactly:One note on how that test is written. My first version changed the process cwd, which passed on its own but broke
test_merge_fastforwardwhen the suite ran in parallel, since cwd is process-wide. The version here passes the doomed directory as the stored path instead, which is the same trap without touching global state.Separate bug found while testing, not fixed here
Opening the Log tab after this scenario still panics, via gix rather than git2:
I confirmed it is independent by reproducing it on an unmodified binary using an explicit absolute
-d, a path this change never touches.gix_repodiscovery consults the process cwd itself, soRepoPathcannot fix it. Happy to file it separately.I followed the checklist:
asyncgit/src/sync/stash.rs, described above)make checkwithout errors (fmtandclippyclean;cargo nextest run --workspaceis 318/319, the one failure beinggit2-hooks tests::test_pre_commit_py, which needs apythonbinary and fails identically on a clean master checkout here.sortanddenyneedtombiandcargo-deny, which are not installed in this environment)asyncgitlayer; I did not drive the whole TUI through this scenario, and the separate gix Log-tab panic noted above is still present)Disclosure: written with AI assistance (Claude Code). I reproduced the issue, ran the change and the verification myself.