From 887e2ea76b4ef47c806e54dde7be553c60c74dc2 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 17 Feb 2026 23:15:16 -0800 Subject: [PATCH] fix: Correct parsing of git output blocks and improve stash UI accessibility --- .../src/routes/worktree/routes/commit-log.ts | 11 ++++- .../src/services/branch-commit-log-service.ts | 32 +++++++++++---- .../dialogs/view-stashes-dialog.tsx | 40 +++++++++---------- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/apps/server/src/routes/worktree/routes/commit-log.ts b/apps/server/src/routes/worktree/routes/commit-log.ts index 1fda67b7..c3a99caa 100644 --- a/apps/server/src/routes/worktree/routes/commit-log.ts +++ b/apps/server/src/routes/worktree/routes/commit-log.ts @@ -48,7 +48,16 @@ export function createCommitLogHandler() { const commitBlocks = logOutput.split('---END---').filter((block) => block.trim()); for (const block of commitBlocks) { - const lines = block.split('\n'); + const allLines = block.split('\n'); + // Skip leading empty lines that result from the split. + // After splitting on ---END---, subsequent blocks start with a newline, + // which creates an empty first element that shifts all field indices + // (hash becomes empty, shortHash becomes hash, etc.). + let startIndex = 0; + while (startIndex < allLines.length && allLines[startIndex].trim() === '') { + startIndex++; + } + const lines = allLines.slice(startIndex); if (lines.length >= 6) { const hash = lines[0].trim(); diff --git a/apps/server/src/services/branch-commit-log-service.ts b/apps/server/src/services/branch-commit-log-service.ts index 2e42a1c9..064b4f16 100644 --- a/apps/server/src/services/branch-commit-log-service.ts +++ b/apps/server/src/services/branch-commit-log-service.ts @@ -69,10 +69,19 @@ export async function getBranchCommitLog( // Parse the output into structured commit objects const commits: BranchCommit[] = []; - const commitBlocks = logOutput.split('---END---\n').filter((block) => block.trim()); + const commitBlocks = logOutput.split('---END---').filter((block) => block.trim()); for (const block of commitBlocks) { - const lines = block.split('\n'); + const allLines = block.split('\n'); + // Skip leading empty lines that result from the split. + // After splitting on ---END---, subsequent blocks start with a newline, + // which creates an empty first element that shifts all field indices + // (hash becomes empty, shortHash becomes hash, etc.). + let startIndex = 0; + while (startIndex < allLines.length && allLines[startIndex].trim() === '') { + startIndex++; + } + const lines = allLines.slice(startIndex); if (lines.length >= 6) { const hash = lines[0].trim(); @@ -80,13 +89,22 @@ export async function getBranchCommitLog( let files: string[] = []; try { const filesOutput = await execGitCommand( - ['diff-tree', '--no-commit-id', '--name-only', '-r', hash], + // -m causes merge commits to be diffed against each parent, + // showing all files touched by the merge (without -m, diff-tree + // produces no output for merge commits because they have 2+ parents) + ['diff-tree', '--no-commit-id', '--name-only', '-r', '-m', hash], worktreePath ); - files = filesOutput - .trim() - .split('\n') - .filter((f) => f.trim()); + // Deduplicate: -m can list the same file multiple times + // (once per parent diff for merge commits) + files = [ + ...new Set( + filesOutput + .trim() + .split('\n') + .filter((f) => f.trim()) + ), + ]; } catch { // Ignore errors getting file list } diff --git a/apps/ui/src/components/views/board-view/dialogs/view-stashes-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/view-stashes-dialog.tsx index 0b119ed7..35887ee9 100644 --- a/apps/ui/src/components/views/board-view/dialogs/view-stashes-dialog.tsx +++ b/apps/ui/src/components/views/board-view/dialogs/view-stashes-dialog.tsx @@ -83,6 +83,7 @@ function StashEntryItem({ }) { const [expanded, setExpanded] = useState(false); const isBusy = isApplying || isDropping; + const hasFiles = stash.files && stash.files.length > 0; // Clean up the stash message for display const displayMessage = @@ -97,30 +98,17 @@ function StashEntryItem({ > {/* Header */}
- {/* Expand toggle & stash icon */} - +
{/* Content */}

{displayMessage}

-
+
stash@{'{' + stash.index + '}'} @@ -143,11 +131,21 @@ function StashEntryItem({ {formatRelativeDate(stash.date)} - {stash.files.length > 0 && ( - + {hasFiles && ( + )}
@@ -191,7 +189,7 @@ function StashEntryItem({
{/* Expanded file list */} - {expanded && stash.files.length > 0 && ( + {expanded && hasFiles && (
{stash.files.map((file) => (