mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-20 23:13:07 +00:00
fix: Correct parsing of git output blocks and improve stash UI accessibility
This commit is contained in:
@@ -48,7 +48,16 @@ export function createCommitLogHandler() {
|
|||||||
const commitBlocks = logOutput.split('---END---').filter((block) => block.trim());
|
const commitBlocks = logOutput.split('---END---').filter((block) => block.trim());
|
||||||
|
|
||||||
for (const block of commitBlocks) {
|
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) {
|
if (lines.length >= 6) {
|
||||||
const hash = lines[0].trim();
|
const hash = lines[0].trim();
|
||||||
|
|
||||||
|
|||||||
@@ -69,10 +69,19 @@ export async function getBranchCommitLog(
|
|||||||
// Parse the output into structured commit objects
|
// Parse the output into structured commit objects
|
||||||
const commits: BranchCommit[] = [];
|
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) {
|
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) {
|
if (lines.length >= 6) {
|
||||||
const hash = lines[0].trim();
|
const hash = lines[0].trim();
|
||||||
|
|
||||||
@@ -80,13 +89,22 @@ export async function getBranchCommitLog(
|
|||||||
let files: string[] = [];
|
let files: string[] = [];
|
||||||
try {
|
try {
|
||||||
const filesOutput = await execGitCommand(
|
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
|
worktreePath
|
||||||
);
|
);
|
||||||
files = filesOutput
|
// Deduplicate: -m can list the same file multiple times
|
||||||
.trim()
|
// (once per parent diff for merge commits)
|
||||||
.split('\n')
|
files = [
|
||||||
.filter((f) => f.trim());
|
...new Set(
|
||||||
|
filesOutput
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter((f) => f.trim())
|
||||||
|
),
|
||||||
|
];
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore errors getting file list
|
// Ignore errors getting file list
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ function StashEntryItem({
|
|||||||
}) {
|
}) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const isBusy = isApplying || isDropping;
|
const isBusy = isApplying || isDropping;
|
||||||
|
const hasFiles = stash.files && stash.files.length > 0;
|
||||||
|
|
||||||
// Clean up the stash message for display
|
// Clean up the stash message for display
|
||||||
const displayMessage =
|
const displayMessage =
|
||||||
@@ -97,30 +98,17 @@ function StashEntryItem({
|
|||||||
>
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-start gap-3 p-3">
|
<div className="flex items-start gap-3 p-3">
|
||||||
{/* Expand toggle & stash icon */}
|
{/* Stash icon (static) */}
|
||||||
<button
|
<div className="flex items-center pt-0.5 text-muted-foreground">
|
||||||
onClick={() => setExpanded(!expanded)}
|
|
||||||
className="flex items-center gap-1 pt-0.5 text-muted-foreground hover:text-foreground transition-colors"
|
|
||||||
disabled={stash.files.length === 0}
|
|
||||||
>
|
|
||||||
{stash.files.length > 0 ? (
|
|
||||||
expanded ? (
|
|
||||||
<ChevronDown className="w-3.5 h-3.5" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="w-3.5 h-3.5" />
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<span className="w-3.5" />
|
|
||||||
)}
|
|
||||||
<Archive className="w-3.5 h-3.5" />
|
<Archive className="w-3.5 h-3.5" />
|
||||||
</button>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="text-sm font-medium leading-snug break-words">{displayMessage}</p>
|
<p className="text-sm font-medium leading-snug break-words">{displayMessage}</p>
|
||||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 mt-1 text-xs text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1.5 text-xs text-muted-foreground">
|
||||||
<span className="inline-flex items-center gap-1 font-mono bg-muted px-1.5 py-0.5 rounded text-[10px]">
|
<span className="inline-flex items-center gap-1 font-mono bg-muted px-1.5 py-0.5 rounded text-[10px]">
|
||||||
stash@{'{' + stash.index + '}'}
|
stash@{'{' + stash.index + '}'}
|
||||||
</span>
|
</span>
|
||||||
@@ -143,11 +131,21 @@ function StashEntryItem({
|
|||||||
{formatRelativeDate(stash.date)}
|
{formatRelativeDate(stash.date)}
|
||||||
</time>
|
</time>
|
||||||
</span>
|
</span>
|
||||||
{stash.files.length > 0 && (
|
{hasFiles && (
|
||||||
<span className="inline-flex items-center gap-1">
|
<button
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
className="inline-flex items-center gap-1 hover:text-foreground transition-colors cursor-pointer"
|
||||||
|
aria-expanded={expanded}
|
||||||
|
aria-label={`${expanded ? 'Collapse' : 'Expand'} file list, ${stash.files.length} file${stash.files.length !== 1 ? 's' : ''}`}
|
||||||
|
>
|
||||||
|
{expanded ? (
|
||||||
|
<ChevronDown className="w-3 h-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="w-3 h-3" />
|
||||||
|
)}
|
||||||
<FileText className="w-3 h-3" />
|
<FileText className="w-3 h-3" />
|
||||||
{stash.files.length} file{stash.files.length !== 1 ? 's' : ''}
|
{stash.files.length} file{stash.files.length !== 1 ? 's' : ''}
|
||||||
</span>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -191,7 +189,7 @@ function StashEntryItem({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Expanded file list */}
|
{/* Expanded file list */}
|
||||||
{expanded && stash.files.length > 0 && (
|
{expanded && hasFiles && (
|
||||||
<div className="border-t px-3 py-2 bg-muted/30">
|
<div className="border-t px-3 py-2 bg-muted/30">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
{stash.files.map((file) => (
|
{stash.files.map((file) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user