refactor(status): Support batch and all changes discard functionality
The file discarding mechanism has been significantly refactored to improve handling of multi-file operations. Instead of processing discards on a per-file basis, the system now supports batch actions for selected files or reverting all tracked modifications simultaneously. This update centralizes complex discard logic into dedicated functions across the component suite. - Updated state management and types to handle arrays of file changes. - Added UI elements and handlers for discarding all staged/unstaged changes. - Enhanced the confirmation dialog to display multiple discarded targets in a list view.
This commit is contained in:
+48
-11
@@ -133,7 +133,8 @@
|
||||
type AppView = "management" | "repository";
|
||||
type CredentialAction = "push" | "pull" | "fetch" | "clone";
|
||||
type PendingDiscard =
|
||||
| { kind: "file"; file: GitFileStatus; staged: boolean }
|
||||
| { kind: "file"; files: GitFileStatus[]; staged: boolean }
|
||||
| { kind: "all-changes"; files: GitFileStatus[] }
|
||||
| { kind: "hunk"; file: GitFileStatus; staged: boolean; action: PatchApplyAction; patch: string };
|
||||
|
||||
interface RepoTab {
|
||||
@@ -2588,26 +2589,59 @@
|
||||
});
|
||||
}
|
||||
|
||||
function discardFile(file: GitFileStatus, staged: boolean) {
|
||||
if (!activeRepoPath || isBusy) return;
|
||||
pendingDiscard = { kind: "file", file, staged };
|
||||
function discardFiles(files: GitFileStatus[], staged: boolean) {
|
||||
if (!activeRepoPath || isBusy || files.length === 0) return;
|
||||
pendingDiscard = { kind: "file", files, staged };
|
||||
trackEvent("discard_confirm_opened", {
|
||||
kind: "file",
|
||||
staged: staged ? 1 : 0,
|
||||
files: files.length,
|
||||
});
|
||||
}
|
||||
|
||||
async function runDiscardFile(file: GitFileStatus, staged: boolean) {
|
||||
await runOperation(`Discarding ${file.path}`, async () => {
|
||||
applyStatus(await restoreFiles(activeRepoPath, [file.path], staged));
|
||||
function discardChanges(files: GitFileStatus[]) {
|
||||
if (!activeRepoPath || isBusy || files.length === 0) return;
|
||||
pendingDiscard = { kind: "all-changes", files };
|
||||
trackEvent("discard_confirm_opened", {
|
||||
kind: "all",
|
||||
files: files.length,
|
||||
});
|
||||
}
|
||||
|
||||
async function runDiscardFiles(files: GitFileStatus[], staged: boolean) {
|
||||
if (files.length === 0) return;
|
||||
const paths = files.map((file) => file.path);
|
||||
await runOperation(files.length === 1 ? `Discarding ${baseName(files[0].path)}` : `Discarding ${files.length} files`, async () => {
|
||||
applyStatus(await restoreFiles(activeRepoPath, paths, staged));
|
||||
await refreshExplorerFiles(activeRepoPath);
|
||||
await refreshFileHistory(activeRepoPath);
|
||||
trackEvent("file_discarded", {
|
||||
files: files.length,
|
||||
staged: staged ? 1 : 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Discards both the staged and unstaged changes for each given file (used
|
||||
// by "Discard all" and "Discard selected", which don't distinguish lanes).
|
||||
async function runDiscardAllChanges(files: GitFileStatus[]) {
|
||||
const stagedPaths = files.filter((file) => file.staged !== null).map((file) => file.path);
|
||||
const unstagedPaths = files.filter((file) => file.unstaged !== null).map((file) => file.path);
|
||||
if (stagedPaths.length === 0 && unstagedPaths.length === 0) return;
|
||||
await runOperation(files.length === 1 ? `Discarding ${baseName(files[0].path)}` : `Discarding ${files.length} files`, async () => {
|
||||
let nextStatus: GitStatus | null = null;
|
||||
if (stagedPaths.length > 0) nextStatus = await restoreFiles(activeRepoPath, stagedPaths, true);
|
||||
if (unstagedPaths.length > 0) nextStatus = await restoreFiles(activeRepoPath, unstagedPaths, false);
|
||||
if (nextStatus) applyStatus(nextStatus);
|
||||
await refreshExplorerFiles(activeRepoPath);
|
||||
await refreshFileHistory(activeRepoPath);
|
||||
trackEvent("file_discarded", {
|
||||
files: files.length,
|
||||
staged: 2,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function openLinePatch(file: GitFileStatus, staged: boolean) {
|
||||
if (!activeRepoPath) return;
|
||||
linePatchOpen = true;
|
||||
@@ -2743,7 +2777,9 @@
|
||||
if (!discard || !activeRepoPath || isBusy) return;
|
||||
|
||||
if (discard.kind === "file") {
|
||||
await runDiscardFile(discard.file, discard.staged);
|
||||
await runDiscardFiles(discard.files, discard.staged);
|
||||
} else if (discard.kind === "all-changes") {
|
||||
await runDiscardAllChanges(discard.files);
|
||||
} else {
|
||||
await runLinePatchAction(discard.action, discard.patch, discard.file, discard.staged);
|
||||
}
|
||||
@@ -3750,7 +3786,8 @@
|
||||
onSelectFile={selectFileFromStatus}
|
||||
onStage={stageFile}
|
||||
onUnstage={unstageFile}
|
||||
onDiscard={discardFile}
|
||||
onDiscard={discardFiles}
|
||||
onDiscardMany={discardChanges}
|
||||
onPatch={openLinePatch}
|
||||
onStageAll={stageAllFiles}
|
||||
onUnstageAll={unstageAllFiles}
|
||||
@@ -3927,8 +3964,8 @@
|
||||
|
||||
{#if pendingDiscard}
|
||||
<DiscardConfirmDialog
|
||||
file={pendingDiscard.file}
|
||||
staged={pendingDiscard.staged}
|
||||
files={pendingDiscard.kind === "hunk" ? [pendingDiscard.file] : pendingDiscard.files}
|
||||
staged={pendingDiscard.kind === "all-changes" ? null : pendingDiscard.staged}
|
||||
scope={pendingDiscard.kind === "hunk" ? "hunk" : "file"}
|
||||
{isBusy}
|
||||
onConfirm={confirmDiscard}
|
||||
|
||||
Reference in New Issue
Block a user