Skip to content

fix(desktop): non-blocking rebase conflict handoff + word-diff freeze guard#129

Open
devlint wants to merge 1 commit into
mainfrom
fix/128-rebase-conflict-ux
Open

fix(desktop): non-blocking rebase conflict handoff + word-diff freeze guard#129
devlint wants to merge 1 commit into
mainfrom
fix/128-rebase-conflict-ux

Conversation

@devlint

@devlint devlint commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #128 — two of the three reported issues; the third (MergeEditor "clunky" UI) is intentionally scoped out, see below.

  • Non-blocking conflict handoff: when an interactive rebase pauses on a genuine merge conflict, RebaseEditor now closes its blocking modal and hands off to the existing non-blocking rebase banner + inline MergeEditor, instead of trapping the user behind an overlay with no resolution UI. edit/split halts (not a conflict) are unaffected — the modal still opens for those, with its "Split this commit…" affordance intact.
  • Rebase-aware finalize: resolving the last conflicted file mid-rebase no longer triggers a spurious git merge --continue error; the rebase banner's own Continue button is now the sole finalize path during a rebase (this also fixes a pre-existing latent bug affecting plain pull --rebase conflicts).
  • Freeze fix: diffHighlight.ts's word-level LCS highlighting is unguarded O(m·n) — a conflicting line with thousands of tokens (minified bundle, long JSON, base64) could blow up the DP table and freeze the UI on every resolution click. Added a WORD_DIFF_MAX_CELLS = 1,000,000 threshold guard that falls back to whole-line highlighting above it, mirroring the guard already used in packages/core/src/diff/lcs.ts for a different LCS shape.

Scoped out: issue #128 also described the resolution UI (MergeEditor.vue) as generally "clunky." After review there's no single, cheap, unambiguously-broken affordance to fix there — it's a diffuse complaint about several overlapping resolution mechanisms. A redesign belongs in its own chantier, not bundled into this bug-fix PR.

Known limitation (documented in ROADMAP.md): after this handoff closes the modal on a conflict, a later edit/split halt within the same rebase won't re-surface the "Split this commit…" affordance — only the banner's Continue/Skip/Abort. Still strictly better than the prior always-blocking modal; tracked as a follow-up.

Test plan

  • pnpm test — 84 files / 651 tests pass (includes new diffHighlight.test.ts regression test for the freeze fix)
  • vue-tsc --noEmit — clean
  • pnpm test:parity — pre-existing unrelated failure only (scan-secrets.test.mjs, stale prebuilt Rust binary predating the secrets scanner; reproduces identically on main, confirmed via stash A/B)
  • Manual QA in a real browser (pnpm dev:web, real git backend, no mocking) against throwaway temp repos:
    • Conflict halt closes the modal, shows the rebase banner, opens MergeEditor inline — nothing blocked
    • Resolving + banner Continue finishes the rebase cleanly, no merge-continue error (verified via git status/git log)
    • edit/split halt (no conflict) still keeps the modal open with Split intact — no regression
    • A ~39 KB / 5000-token conflicting line falls back to whole-line highlighting and stays responsive (~7-8ms frame time, vs. the prior freeze)
    • Probed: aborting mid-handoff recovers cleanly; a second conflict later in the same rebase is handled by the banner (no modal left to trap it)
  • A real-git-repo automated test for the useInteractiveRebase conflict signal was attempted but destabilized the parallel test suite (confirmed via A/B against main) without adding real coverage beyond the manual QA above — dropped rather than left flaky or backed by a mocked git layer.

… guard

Closes the interactive-rebase modal on a conflict halt so the existing
non-blocking rebase banner + inline MergeEditor take over instead of
trapping resolution behind the overlay. Also guards diffHighlight.ts's
word-level LCS against pathological line lengths (minified/base64 lines)
that could freeze the main thread on every resolution click.

- RebaseEditor.vue emits a dedicated `conflict` event on conflict halts
  only (edit/split halts keep the modal open, unchanged).
- App.vue's onRebaseConflict() closes the modal, refreshes repo state,
  and opens MergeEditor on the first conflicted file.
- advanceToNextConflictOrFinalize() is now rebase-aware, avoiding a
  spurious `git merge --continue` error mid-rebase.
- diffHighlight.ts falls back to whole-line highlighting above a
  1,000,000-cell token-product threshold instead of computing the full
  O(m*n) LCS.

Fixes #128
@devlint
devlint requested a review from t1gu1 July 17, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Clunky conflict resolution UX and non-interactive rebase modal

1 participant