feat(desktop): show ahead/behind status on local workspace#1496
Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds ahead/behind commit counting to workspace git status: new git utility, backend procedure, schema fields, UI components, and sidebar integration to fetch and display ahead/behind counts and derive rebase-needed from behind. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as WorkspaceListItem / HoverCard
participant API as trpc getAheadBehind / git-status proc
participant Git as getAheadBehindCount
participant DB as Local DB
User->>UI: Hover or open workspace list
UI->>API: request ahead/behind (workspaceId)
API->>DB: resolve workspace & repo info
API->>Git: getAheadBehindCount(repoPath, defaultBranch)
Git->>Git: run `git rev-list --left-right --count origin/<defaultBranch>...HEAD`
Git-->>API: { ahead: n, behind: m }
API-->>UI: { ahead: n, behind: m, needsRebase: m > 0 }
UI->>UI: render WorkspaceAheadBehind and HoverCard message
UI-->>User: display ahead/behind indicators and rebase status
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts`:
- Around line 85-102: The getAheadBehind procedure calls getAheadBehindCount
with repoPath: project.mainRepoPath and defaultBranch: workspace.branch but
getAheadBehindCount currently compares origin/{branch}...HEAD which is only
correct when the main repo's HEAD is on workspace.branch; change the call and/or
getAheadBehindCount signature to accept an explicit localBranch ref (e.g.,
localBranch: workspace.branch) and have getAheadBehindCount compare
origin/{branch}...refs/heads/{localBranch} (or otherwise use the explicit branch
ref instead of implicit HEAD) so the ahead/behind numbers reflect the specified
workspace.branch; keep refreshGitStatus behavior as-is since worktrees rely on
HEAD.
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts`:
- Around line 1093-1116: Remove the dead function checkNeedsRebase from the file
(it’s no longer referenced now that getAheadBehindCount drives needsRebase via
behind > 0); locate the checkNeedsRebase declaration and delete its entire
definition and any associated unused imports or helper constants only used by it
so there’s no leftover unused symbols (leave getAheadBehindCount intact).
🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx (1)
530-543: Minor UX note: diff stats for branch workspaces appear only after hover, while ahead/behind shows immediately.The
localChangesquery (Line 137) is gated byhasHovered, soWorkspaceDiffStatsfor branch workspaces won't render until the user has hovered at least once. This can cause a subtle layout shift when the stats badge appears. If this is intentional (keeping the sidebar compact by default), that's fine — just calling it out in case it's an oversight.
| getAheadBehind: publicProcedure | ||
| .input(z.object({ workspaceId: z.string() })) | ||
| .query(async ({ input }) => { | ||
| const workspace = getWorkspace(input.workspaceId); | ||
| if (!workspace) { | ||
| return { ahead: 0, behind: 0 }; | ||
| } | ||
|
|
||
| const project = getProject(workspace.projectId); | ||
| if (!project) { | ||
| return { ahead: 0, behind: 0 }; | ||
| } | ||
|
|
||
| return getAheadBehindCount({ | ||
| repoPath: project.mainRepoPath, | ||
| defaultBranch: workspace.branch, | ||
| }); | ||
| }), |
There was a problem hiding this comment.
Verify that getAheadBehind semantics match the intended use for branch workspaces.
The procedure compares origin/{workspace.branch}...HEAD in the main repo. This correctly shows how the local branch relates to its remote tracking branch — but only when the main repo's HEAD is on workspace.branch. If the user switches branches externally (e.g., via terminal), HEAD will refer to a different branch and the numbers will be misleading.
Consider using an explicit branch ref instead of implicit HEAD to make this resilient:
Proposed fix
In getAheadBehindCount (or a variant), replace HEAD with the explicit branch name:
const output = await git.raw([
"rev-list",
"--left-right",
"--count",
- `origin/${defaultBranch}...HEAD`,
+ `origin/${defaultBranch}...${defaultBranch}`,
]);This would require either a separate parameter or passing the local branch name explicitly. The tradeoff is that refreshGitStatus (for worktrees) correctly uses HEAD since the worktree's HEAD is always on its branch.
🤖 Prompt for AI Agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts` around
lines 85 - 102, The getAheadBehind procedure calls getAheadBehindCount with
repoPath: project.mainRepoPath and defaultBranch: workspace.branch but
getAheadBehindCount currently compares origin/{branch}...HEAD which is only
correct when the main repo's HEAD is on workspace.branch; change the call and/or
getAheadBehindCount signature to accept an explicit localBranch ref (e.g.,
localBranch: workspace.branch) and have getAheadBehindCount compare
origin/{branch}...refs/heads/{localBranch} (or otherwise use the explicit branch
ref instead of implicit HEAD) so the ahead/behind numbers reflect the specified
workspace.branch; keep refreshGitStatus behavior as-is since worktrees rely on
HEAD.
| export async function getAheadBehindCount({ | ||
| repoPath, | ||
| defaultBranch, | ||
| }: { | ||
| repoPath: string; | ||
| defaultBranch: string; | ||
| }): Promise<{ ahead: number; behind: number }> { | ||
| const git = simpleGit(repoPath); | ||
| try { | ||
| const output = await git.raw([ | ||
| "rev-list", | ||
| "--left-right", | ||
| "--count", | ||
| `origin/${defaultBranch}...HEAD`, | ||
| ]); | ||
| const [behindStr, aheadStr] = output.trim().split(/\s+/); | ||
| return { | ||
| ahead: Number.parseInt(aheadStr || "0", 10), | ||
| behind: Number.parseInt(behindStr || "0", 10), | ||
| }; | ||
| } catch { | ||
| return { ahead: 0, behind: 0 }; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for all references to checkNeedsRebase in the codebase
rg -n 'checkNeedsRebase' --type=ts --type=tsxRepository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Check the git.ts file for `any` type usage and other potential issues
head -100 apps/desktop/src/lib/trpc/routers/workspaces/utils/git.tsRepository: superset-sh/superset
Length of output: 2626
🏁 Script executed:
# Check if the file uses proper aliases as defined in tsconfig
grep -n "import.*from" apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts | head -20Repository: superset-sh/superset
Length of output: 612
🏁 Script executed:
# Search for all references to checkNeedsRebase without file type restriction
rg -n 'checkNeedsRebase' apps/desktop/src/lib/trpc/routers/workspaces/utils/Repository: superset-sh/superset
Length of output: 166
🏁 Script executed:
# Read lines 1070-1120 to see checkNeedsRebase and the new function in context
sed -n '1070,1120p' apps/desktop/src/lib/trpc/routers/workspaces/utils/git.tsRepository: superset-sh/superset
Length of output: 1144
🏁 Script executed:
# Search for any type usage in git.ts
rg -n ': any' apps/desktop/src/lib/trpc/routers/workspaces/utils/git.tsRepository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Check if checkNeedsRebase is exported from git.ts and imported elsewhere
rg -n 'export.*checkNeedsRebase|import.*checkNeedsRebase' apps/desktop/Repository: superset-sh/superset
Length of output: 166
🏁 Script executed:
# Search entire repository for checkNeedsRebase imports/usage (not just definition)
rg -n 'checkNeedsRebase' --type tsRepository: superset-sh/superset
Length of output: 166
🏁 Script executed:
# Specifically check git-status.ts mentioned in the review
find apps/desktop -name "git-status.ts" -o -name "*git-status*" | head -5Repository: superset-sh/superset
Length of output: 133
🏁 Script executed:
# Look for any references to checkNeedsRebase in the whole repo
rg 'checkNeedsRebase' -g '!git.ts'Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Check git-status.ts to see how getAheadBehindCount is used
cat -n apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts | head -50Repository: superset-sh/superset
Length of output: 1817
🏁 Script executed:
# Also search git-status.ts for any references to checkNeedsRebase or needsRebase
rg -n 'needsRebase|checkNeedsRebase' apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.tsRepository: superset-sh/superset
Length of output: 96
Remove the checkNeedsRebase function as dead code.
The getAheadBehindCount function correctly parses --left-right --count output, mapping the first column (left/remote) to behind and the second column (right/HEAD) to ahead. Silent error fallback to {0, 0} is reasonable for a non-critical status indicator.
The existing checkNeedsRebase function (line 1080) is no longer used—git-status.ts now derives needsRebase from behind > 0 using the new getAheadBehindCount. Remove this function to avoid confusion.
🤖 Prompt for AI Agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts` around lines 1093
- 1116, Remove the dead function checkNeedsRebase from the file (it’s no longer
referenced now that getAheadBehindCount drives needsRebase via behind > 0);
locate the checkNeedsRebase declaration and delete its entire definition and any
associated unused imports or helper constants only used by it so there’s no
leftover unused symbols (leave getAheadBehindCount intact).
Extract WorkspaceIcon and CollapsedWorkspaceItem to reduce the main component from ~700 to ~470 lines. Deduplicate icon rendering, consolidate context menu items, and simplify drag-and-drop handler logic.
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
Summary
↓N/↑N) on branch/local workspace items in the sidebar, comparing against the remote tracking branch (origin/{branch})gitStatusSchemawith optionalahead/behindfields and deriveneedsRebasefrombehind > 0Test plan
↓N/↑Nbadges appear on the local/main workspace item when commits are ahead or behind remotebun run typecheckandbun run lint:fix— both pass cleanlySummary by CodeRabbit
New Features
Refactor