Skip to content

Commit c3e70af

Browse files
matt2eclaude
andauthored
fix(staged): propagate git errors and reload timeline after cache invalidation (#640)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a353c6d commit c3e70af

4 files changed

Lines changed: 32 additions & 10 deletions

File tree

apps/staged/src-tauri/src/timeline.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,10 @@ fn build_branch_timeline(store: &Arc<Store>, branch_id: &str) -> Result<BranchTi
8383
// Local branch: fetch commits from the local worktree
8484
let worktree_path = Path::new(&wd.path);
8585
if worktree_path.exists() {
86-
let git_commits = match git::get_commits_since_base(worktree_path, &branch.base_branch)
87-
{
88-
Ok(commits) => commits,
89-
Err(e) => {
90-
log::warn!("Failed to get commits since base for branch {branch_id}: {e:?}");
91-
vec![]
92-
}
93-
};
86+
let git_commits = git::get_commits_since_base(worktree_path, &branch.base_branch)
87+
.map_err(|e| {
88+
format!("Failed to get commits since base for branch {branch_id}: {e:?}")
89+
})?;
9490

9591
// For each git commit, look up our metadata (session linkage)
9692
for gc in git_commits {

apps/staged/src/lib/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ const inFlightTimelines = new Map<string, Promise<BranchTimeline>>();
320320

321321
export function invalidateBranchTimeline(branchId: string): void {
322322
timelineCache.delete(branchId);
323+
window.dispatchEvent(
324+
new CustomEvent('timeline-invalidated', { detail: { branchIds: [branchId] } })
325+
);
323326
}
324327

325328
interface GetBranchTimelineOptions {
@@ -373,6 +376,7 @@ export function invalidateProjectBranchTimelines(branchIds: string[]): void {
373376
for (const id of branchIds) {
374377
timelineCache.delete(id);
375378
}
379+
window.dispatchEvent(new CustomEvent('timeline-invalidated', { detail: { branchIds } }));
376380
}
377381

378382
// =============================================================================

apps/staged/src/lib/features/branches/BranchCard.svelte

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,18 @@
505505
void loadTimeline();
506506
});
507507
508+
// Re-fetch timeline when the cache is invalidated (e.g. after project-setup-progress)
509+
$effect(() => {
510+
const handler = (e: Event) => {
511+
const { branchIds } = (e as CustomEvent<{ branchIds: string[] }>).detail;
512+
if (branchIds.includes(branch.id) && (branch.worktreePath || isRemote)) {
513+
void loadTimeline();
514+
}
515+
};
516+
window.addEventListener('timeline-invalidated', handler);
517+
return () => window.removeEventListener('timeline-invalidated', handler);
518+
});
519+
508520
let revalidationVersion = 0;
509521
510522
async function loadTimeline() {

apps/staged/src/lib/features/projects/ProjectHome.svelte

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,18 @@
118118
]);
119119
setProjects(projectsList);
120120
projects = projectsList;
121-
branchesByProject = new Map(branchesByProject).set(projectId, branches);
122-
commands.invalidateProjectBranchTimelines(branches.map((b) => b.id));
121+
// Merge branches carefully: don't let a stale async response overwrite
122+
// worktreePath with null when we already have it set.
123+
const existingBranches = branchesByProject.get(projectId) || [];
124+
const mergedBranches = branches.map((newBranch) => {
125+
const existing = existingBranches.find((b) => b.id === newBranch.id);
126+
if (existing?.worktreePath && !newBranch.worktreePath) {
127+
return { ...newBranch, worktreePath: existing.worktreePath };
128+
}
129+
return newBranch;
130+
});
131+
branchesByProject = new Map(branchesByProject).set(projectId, mergedBranches);
132+
commands.invalidateProjectBranchTimelines(mergedBranches.map((b) => b.id));
123133
workspaceLifecycle.enqueueInitialSetup(projectId, branches);
124134
replaceProjectRepos(projectId, repos);
125135
void repoBadgeStore.ensureForRepos(

0 commit comments

Comments
 (0)