Skip to content

Stateless conflict resolution#14617

Open
krlvi wants to merge 3 commits into
masterfrom
stateless-conflict-resolution
Open

Stateless conflict resolution#14617
krlvi wants to merge 3 commits into
masterfrom
stateless-conflict-resolution

Conversation

@krlvi

@krlvi krlvi commented Jul 4, 2026

Copy link
Copy Markdown
Member

Resolve a conflicted commit's conflicts without ever entering edit mode. Everything works from the committed conflict trees in memory, so agents and the UI can inspect and resolve conflicts one at a time without checking anything out.

API (but-api::resolve)

  • commit_conflicts(commit) — read-only: returns each conflicted file's ours/base/theirs hunks, plus a synthetic TreeChange (the base's version vs the commit's own) so a fully-conflicted file still renders a real diff. Returns no files for a non-conflicted commit rather than erroring, so callers can race a just-resolved commit safely.
  • resolve_commit_conflict_hunks(commit, specs) — apply Ours / Theirs / Content / Ai to any subset of hunks. Resolving a subset narrows the conflict and leaves the commit conflicted with the rest; resolving all of them normalizes it. Each apply rewrites the commit and rebases descendants under one oplog snapshot, so a bad resolution is one but undo away.

Resolutions are applied by narrowing: the resolved content goes into the ours and theirs side trees only (the base is left untouched, so a later re-pick replays the resolution instead of silently dropping it), then a force-ours re-merge produces the auto-resolution — never marker text in a tree. Per-hunk AI (HunkResolution::Ai) sends only the addressed hunks to the model and splices the result back like Content.

CLI

  • but resolve conflicts [<commit-or-branch>] — list the conflicts of a conflicted commit.
  • but resolve apply <path>[:<N>] [--ours|--theirs|--ai|--file <f>|stdin] [--commit <commit-or-branch>] — resolve one conflict (or a whole file with a side). Targets are polymorphic: a branch means its oldest conflicted commit, which stays a stable address across the id churn every apply causes.

Desktop

Clicking a conflicted commit now shows a normal diff for every file. Files whose conflict is otherwise hidden by the auto-resolution get their real base-vs-commit diff, with a banner that toggles inline ours/base/theirs conflict cards interleaved at the right position. Each card can resolve that one conflict — use current base, use this commit, edit the merged text, or resolve with AI — and the view follows the rewritten commit so the remaining conflicts stay in front of you until none are left. The old right-click "Show conflicts" modal is removed in favor of the inline view.

Copilot AI review requested due to automatic review settings July 4, 2026 20:15
@github-actions github-actions Bot added rust Pull requests that update Rust code @gitbutler/desktop CLI The command-line program `but` labels Jul 4, 2026

Copilot AI left a comment

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.

Pull request overview

Adds a “stateless” conflict-resolution workflow that lets callers inspect a conflicted commit’s conflict hunks from its stored conflict trees and apply per-hunk resolutions (ours/theirs/custom content/AI) without entering edit mode, rewriting the commit and rebasing descendants under an oplog snapshot.

Changes:

  • Introduces commit_conflicts() and resolve_commit_conflict_hunks() in but-api::resolve (plus schema/SDK types) and wires the new endpoints through Tauri + desktop.
  • Extends the legacy but resolve CLI with conflicts and apply for per-hunk / per-file resolution without checkout/edit mode.
  • Updates desktop diff UX to show inline conflict cards and to surface synthetic diffs for conflict-only files hidden by auto-resolution.

Reviewed changes

Copilot reviewed 25 out of 27 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/but-sdk/src/generated/linear/index.d.ts Adds generated SDK transport types for commit conflicts + per-hunk resolution results/specs.
packages/but-sdk/src/generated/graph/index.d.ts Same generated SDK updates for graph SDK output.
crates/gitbutler-tauri/src/main.rs Registers new Tauri commands for commit_conflicts and resolve_commit_conflict_hunks.
crates/gitbutler-oplog/src/entry.rs Adds ResolveConflicts operation kind + display/serialization mappings.
crates/but/src/command/legacy/resolve.rs Adds resolve conflicts listing and resolve apply per-hunk/per-file resolution flow.
crates/but/src/command/legacy/oplog.rs Colors/prints the new ResolveConflicts operation in oplog output.
crates/but/src/args/resolve.rs Adds Conflicts and Apply subcommands + CLI flag semantics.
crates/but/skill/SKILL.md Updates resolution guidance to prefer stateless per-hunk resolution workflow.
crates/but-api/tests/fixtures/scenario/resolve-ai-conflicted-commit.sh Expands fixture to produce two separated conflict hunks + new-base parent setup.
crates/but-api/tests/api/resolve_hunks.rs New tests covering commit_conflicts + partial/full per-hunk apply + oplog snapshots.
crates/but-api/tests/api/resolve_ai.rs Updates AI resolution tests to handle multiple hunks and new prompt structs.
crates/but-api/tests/api/main.rs Registers the new resolve-hunks test module.
crates/but-api/src/resolve/prompt.rs Renames AI output hunk struct (HunkResolutionHunkContent) for clarity.
crates/but-api/src/resolve/mod.rs Implements commit_conflicts + per-hunk resolution API + shared AI validation/retry.
crates/but-api/src/resolve/context.rs Extends extracted hunks with an auto-resolution line anchor + exports schema.
crates/but-api/src/resolve/apply.rs Reworks apply pipeline to “narrow” conflict trees and re-merge, supporting partial resolution.
apps/desktop/src/lib/stacks/stackService.svelte.ts Adds client helpers for commitConflicts query + per-hunk resolve mutation.
apps/desktop/src/lib/stacks/stackEndpoints.ts Defines the new backend endpoints and caching/invalidations.
apps/desktop/src/lib/stacks/stackController.svelte.ts Adds diff jumping by path and a safe maybeGetStackContext().
apps/desktop/src/components/views/StackDetails.svelte Fetches commit conflicts and passes them into MultiDiffView for inline rendering.
apps/desktop/src/components/views/BranchCommitList.svelte Stops hiding conflicted files from “Changed files” list; wires conflicted-file click to diff jump.
apps/desktop/src/components/files/FileListConflicts.svelte Adds optional click override to open diff instead of prompting edit-mode entry.
apps/desktop/src/components/files/ChangedFilesPanel.svelte Plumbs onConflictedFileClick through to FileListConflicts.
apps/desktop/src/components/diff/UnifiedDiffView.svelte Renders inline conflict cards alongside normal diff hunks with toggle + bucketing by line anchor.
apps/desktop/src/components/diff/SelectionView.svelte Fetches commit conflicts for selected commit file and passes hunks to the unified diff view.
apps/desktop/src/components/diff/MultiDiffView.svelte Appends synthetic conflict-only changes, supports jump-to-path, and passes per-file conflict hunks down.
apps/desktop/src/components/diff/ConflictHunkCard.svelte New UI component to view/resolve a single conflict hunk (ours/theirs/edit/AI) and follow rewritten commit IDs.

Comment thread crates/but-api/src/resolve/apply.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38df7c43c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/but/src/command/legacy/resolve.rs Outdated
Comment thread crates/but-api/src/resolve/apply.rs
krlvi added 2 commits July 4, 2026 22:23
Two new APIs on the conflict primitives: but_api::resolve::commit_conflicts
returns a conflicted commit's conflicts as per-file ours/base/theirs hunks,
and resolve_commit_conflict_hunks applies side picks or custom content to
any subset of them. Partial resolutions narrow the conflict: each resolved
hunk is written into all three side trees and the re-merge conflicts only
at what remains, so the commit stays conflicted with fewer conflicts and
work can proceed incrementally. Full coverage produces a normal commit via
the same path, and the AI resolver now runs on this core.

Exposed as `but resolve conflicts` and `but resolve apply <path>[:<hunk>]`
(side flags or stdin/file content, defaulting to the oldest conflicted
commit), as a read-only conflicts modal on right-click in the desktop app.
The agent skill now steers coding agents to this stateless flow, with
edit mode reserved for when the commit must be checked out.
Clicking a conflicted commit now renders a real diff for every conflicted
file. commit_conflicts returns a synthetic TreeChange per file diffing the
current base's version (ours) against the commit's own (theirs) — regular
commit diffs are computed against the auto-resolution, which hides exactly
the conflicted content. The diff view marks such files with a banner and a
Show/Hide toggle that interleaves ours/base/theirs conflict cards at the
right position via a new ConflictHunk.line anchor. The file list no longer
filters conflicted files, and clicking an unrepresented one jumps to its
entry in the diff pane. The right-click 'Show conflicts' modal is
superseded and removed.
@krlvi krlvi force-pushed the stateless-conflict-resolution branch from 38df7c4 to c629c58 Compare July 4, 2026 20:24
Copilot AI review requested due to automatic review settings July 4, 2026 20:28
@krlvi krlvi force-pushed the stateless-conflict-resolution branch from c629c58 to 1b94f32 Compare July 4, 2026 20:28

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 25 out of 27 changed files in this pull request and generated 1 comment.

Comment thread crates/but/src/command/legacy/resolve.rs Outdated
@krlvi krlvi force-pushed the stateless-conflict-resolution branch from 1b94f32 to c6b1b24 Compare July 4, 2026 20:37
Each inline conflict card now offers Use current base, Use this commit,
Edit (a prefilled textarea for a mixed resolution), and Resolve with AI.
Every action applies immediately through resolve_commit_conflict_hunks and
narrows the conflict; the selection follows the rewritten commit so the
remaining conflicts stay in view until none are left.

Per-conflict AI is a new HunkResolution::Ai variant on the same API: AI
specs are sent to the model as a request narrowed to just those hunks and
applied like content resolutions, so sides, content, and AI can mix in one
call and AI configuration is only required when actually used. Also
exposed as 'but resolve apply <path>:<N> --ai'.
Copilot AI review requested due to automatic review settings July 4, 2026 20:44
@krlvi krlvi force-pushed the stateless-conflict-resolution branch from c6b1b24 to d1c0f7d Compare July 4, 2026 20:44

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 25 out of 27 changed files in this pull request and generated no new comments.

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

Labels

CLI The command-line program `but` @gitbutler/desktop rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants