Skip to content

feat(git): friendly checkout error messages with stash & switch recovery#1785

Open
Marve10s wants to merge 5 commits intopingdotgg:mainfrom
Marve10s:feat/checkout-dirty-worktree-error-handling
Open

feat(git): friendly checkout error messages with stash & switch recovery#1785
Marve10s wants to merge 5 commits intopingdotgg:mainfrom
Marve10s:feat/checkout-dirty-worktree-error-handling

Conversation

@Marve10s
Copy link
Copy Markdown
Contributor

@Marve10s Marve10s commented Apr 6, 2026

Summary

  • When git checkout fails because the working tree has uncommitted changes, the app now shows a friendly "Uncommitted changes block checkout" error toast listing the conflicting files — instead of dumping a raw GitCommandError stack trace with internal file paths
  • The error toast includes a "Stash & Switch" action button that stashes changes, checks out the target branch, and attempts to pop the stash on the new branch
  • If the stash cannot be applied cleanly (e.g. the target branch doesn't have the same files), the server resets the merge state, cleans up orphaned untracked files, and keeps the stash entry safe. The UI shows a warning toast with a "Discard stash" button (with confirmation dialog) to drop the stash
  • Both "Your local changes would be overwritten" and "untracked working tree files would be overwritten" git error variants are now detected and handled with the same friendly UX
  • The "you need to resolve your current index" error (which previously showed a raw stack trace) now shows a clear "Unresolved conflicts in the repository" message
  • Toast layout restructured: copy and action buttons now render below the description instead of floating inline with the title. Copy button extended to warning-type toasts
  • Adds GitCheckoutDirtyWorktreeError tagged error, stashAndCheckout and stashDrop WS methods end-to-end (contracts → server → client)

Note

Medium Risk
Introduces new git checkout error typing and a stash/pop workflow exposed over WebSocket RPC; failures could affect branch switching UX and leave repos in unexpected state if edge cases aren’t covered.

Overview
Improves branch switching when git checkout is blocked by a dirty worktree. Server-side checkoutBranch now detects both tracked and untracked overwrite failures and returns a structured GitCheckoutDirtyWorktreeError containing the conflicting file list.

Adds stashAndCheckout (stash with a descriptive message → checkout → stash pop) plus stashDrop, including cleanup logic when stash pop fails (reset/restore/clean untracked remnants) and tests covering success, checkout failure rollback, and conflict scenarios.

Wires the new flow end-to-end via contracts + WS RPC (git.stashAndCheckout, git.stashDrop) and updates the branch selector UI to show a friendlier toast with a “Stash & Switch” action (and “Discard stash” on stash-apply conflicts). Toast layout is adjusted so copy/action buttons render below the description, and copy-to-clipboard is available for warning toasts as well.

Reviewed by Cursor Bugbot for commit 3662ac5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add stash & switch recovery for dirty worktree errors on branch checkout

  • checkoutBranch in GitCore.ts now returns a typed GitCheckoutDirtyWorktreeError (with conflicting file list) instead of a generic failure when uncommitted changes block checkout.
  • Adds stashAndCheckout and stashDrop operations to the GitCore service, exposed as WebSocket RPCs and wired through to environmentApi.ts.
  • BranchToolbarBranchSelector.tsx now intercepts dirty worktree errors and offers a "Stash & Switch" toast action; if the stash pop conflicts, a follow-up "Discard stash" action is offered.
  • Warning toasts in toast.tsx now show a copy button and render action buttons inline below the description.
  • Behavioral Change: checkoutBranch callers now receive a union of GitCommandError | GitCheckoutDirtyWorktreeError; createBranch maps the new error back to GitCommandError to preserve existing behavior.

Macroscope summarized e3db0ea.

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 6, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 53b0c5a8-9dd2-4005-83b4-2528a1492115

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Marve10s Marve10s force-pushed the feat/checkout-dirty-worktree-error-handling branch 2 times, most recently from 0ed6af8 to 6709644 Compare April 6, 2026 17:38
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 6, 2026

Approvability

Verdict: Needs human review

This PR introduces new user-facing git functionality with stash-and-switch recovery workflows, new RPC methods, and substantial frontend logic for handling checkout errors. New features with runtime behavior changes require human review. Additionally, there's an unresolved review comment about error handling in the recovery flow.

You can customize Macroscope's approvability policy. Learn more.

@Marve10s Marve10s force-pushed the feat/checkout-dirty-worktree-error-handling branch 2 times, most recently from ebe2610 to 0ff3bdc Compare April 6, 2026 17:47
@Marve10s Marve10s force-pushed the feat/checkout-dirty-worktree-error-handling branch 2 times, most recently from 0252c65 to 042a9d9 Compare April 6, 2026 18:12
@Marve10s
Copy link
Copy Markdown
Contributor Author

Marve10s commented Apr 6, 2026

Before :
before-1-dirty-checkout-raw-error
before-2-untracked-raw-error

After :
after-1-friendly-error-with-stash-button
after-2-stash-conflict-warning-with-discard
If users tried to discard stash - shows a modal with approval.

Replace raw GitCommandError stack traces with structured, user-friendly
error handling when branch checkout fails due to uncommitted changes.
@Marve10s Marve10s force-pushed the feat/checkout-dirty-worktree-error-handling branch from 042a9d9 to c3cac0a Compare April 6, 2026 18:17
@sashaduke
Copy link
Copy Markdown

+1, I have an error of a different kind that spills out so much i cant even close the toast so im gonna make a similar PR but this is useful

Resolve merge conflicts:
- BranchToolbarBranchSelector.tsx: adapt imports to LocalApi/EnvironmentApi split
- ipc.ts: move stashAndCheckout/stashDrop to EnvironmentApi.git (not LocalApi)
- wsNativeApi.ts: accept upstream deletion (replaced by localApi.ts + environmentApi.ts)
@github-actions github-actions bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Apr 9, 2026
- Add stashAndCheckout/stashDrop to EnvironmentApi bindings and RPC wiring
- Replace useOptimistic with useState to work without useTransition
- Wrap toast actions in runBranchAction for concurrency safety
- Pass environmentId through checkout error context for query invalidation
- Add missing Cause import for GitCore stash error handling
- Use --include-untracked in stash cleanup to capture all remnants
@Marve10s Marve10s force-pushed the feat/checkout-dirty-worktree-error-handling branch from 3662ac5 to e3db0ea Compare April 9, 2026 15:08
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e3db0ea. Configure here.

});
},
},
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors inside stash-conflict handler silently swallowed by outer catch

Low Severity

Inside the catch (stashError) block, when isStashConflictError is true, invalidateGitQueries and ctx.onSuccess() are called before showing the stash-conflict toast. If either throws, the exception escapes the catch block, propagates out of the async action, and is silently swallowed by action().catch(() => undefined) in runBranchAction. The user sees no toast at all, and ctx.onSuccess() may not fire, leaving the optimistic branch state stuck on previousBranch even though the checkout actually succeeded.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e3db0ea. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants