feat(git): add stash push with optional paths and per-file scope

The stash push feature now supports limiting the stash to
selected files via an optional paths parameter.
The UI passes file paths to stash_push and adds a
per-file context menu for scoped stash operations.

- Extend stash API to accept optional paths for scoped stashes
- Implement per-file stash actions via a status panel context menu
- Update tests and docs to reflect scoped stash behavior
This commit is contained in:
Christoph Brandau
2026-08-17 22:24:22 +02:00
parent e7fbaadf5e
commit b5d15f9190
8 changed files with 328 additions and 20 deletions
+15 -4
View File
@@ -3852,11 +3852,15 @@
}
}
async function saveStash(message: string, includeUntracked: boolean) {
if (!activeRepoPath || changedFiles.length === 0) return;
const stashedFiles = changedFiles.length;
async function saveStash(message: string, includeUntracked: boolean, files?: GitFileStatus[]) {
const targets = files ?? changedFiles;
if (!activeRepoPath || targets.length === 0) return;
const paths = files
? [...new Set(files.flatMap((file) => file.old_path ? [file.old_path, file.path] : [file.path]))]
: undefined;
const stashedFiles = targets.length;
await runOperation("Stashing changes", async () => {
applyStatus(await stashPush(activeRepoPath, message, includeUntracked));
applyStatus(await stashPush(activeRepoPath, message, includeUntracked, paths));
await refreshRepositoryViews(activeRepoPath, {
branches: false,
stashes: true,
@@ -3865,10 +3869,16 @@
trackEvent("stash_saved", {
include_untracked: includeUntracked ? 1 : 0,
changed_files: stashedFiles,
scoped: files ? 1 : 0,
});
});
}
async function stashStatusFiles(files: GitFileStatus[], label: string) {
const suffix = files.length === 1 ? files[0].path : `${label} (${files.length} files)`;
await saveStash(`Gitty: ${suffix}`, true, files);
}
async function applyStashEntry(stash: GitStash) {
if (!activeRepoPath) return;
await runOperation(`Applying ${stash.selector}`, async () => {
@@ -5367,6 +5377,7 @@
onUnstage={unstageFile}
onDiscard={discardFiles}
onDiscardMany={discardChanges}
onStash={stashStatusFiles}
onPatch={openPreferredFileDiff}
onStageAll={stageAllFiles}
onUnstageAll={unstageAllFiles}