feat(git): add rebase workflow with UI state handling

This introduces Git rebase commands (start, continue, abort) and
exposes whether a rebase is currently in progress via the status
payload. The UI now blocks committing during an active rebase and
shows a dedicated rebase notice with continue/abort actions.

- Add tauri commands for rebase operations and status detection
- Update frontend to render rebase state and controls
- Adjust conflict/resolution messaging and related UI styling
This commit is contained in:
Christoph Brandau
2026-07-04 00:34:36 +02:00
parent 1d67312ee4
commit 3c4425a408
9 changed files with 273 additions and 18 deletions
+10
View File
@@ -49,6 +49,7 @@
isBusy: boolean;
onCheckout: (branch: GitBranchInfo) => void;
onMerge: (branch: GitBranchInfo) => void;
onRebase: (branch: GitBranchInfo) => void;
onCreateBranch: (branchName: string) => void | Promise<void>;
onRenameBranch: (branch: GitBranchInfo) => void | Promise<void>;
onDeleteBranch: (branch: GitBranchInfo) => void | Promise<void>;
@@ -62,6 +63,7 @@
isBusy = false,
onCheckout = () => {},
onMerge = () => {},
onRebase = () => {},
onCreateBranch = () => {},
onRenameBranch = () => {},
onDeleteBranch = () => {},
@@ -370,6 +372,10 @@
<GitMerge size={15} aria-hidden="true" />
Merge
</button>
<button class="btn-sm" type="button" onclick={() => onRebase(row.branch)} disabled={isBusy} title="Rebase current branch onto this branch">
<GitBranch size={15} aria-hidden="true" />
Rebase
</button>
</div>
{/if}
</article>
@@ -446,6 +452,10 @@
<GitMerge size={15} aria-hidden="true" />
Merge
</button>
<button class="btn-sm" type="button" onclick={() => onRebase(row.branch)} disabled={isBusy} title="Rebase current branch onto this branch">
<GitBranch size={15} aria-hidden="true" />
Rebase
</button>
</div>
{/if}
</article>
+3 -3
View File
@@ -261,13 +261,13 @@
style="grid-template-rows: auto minmax(0,1fr) auto;"
role="dialog"
aria-modal="true"
aria-label="Resolve merge conflicts"
aria-label="Resolve conflicts"
tabindex="-1"
>
<header class="dialog-header">
<div>
<span class="eyebrow">Resolve</span>
<h2 class="dialog-title">Merge conflicts</h2>
<h2 class="dialog-title">Conflicts</h2>
</div>
<button class="dialog-close" type="button" onclick={onClose} title="Close">
<X size={18} aria-hidden="true" />
@@ -275,7 +275,7 @@
</header>
{#if conflictedFiles.length === 0}
<div class="blank-state">All conflicts resolved. You can commit the merge now.</div>
<div class="blank-state">All conflicts resolved. Continue the current operation when ready.</div>
{:else}
<div class="dialog-body">
<aside class="dialog-files" aria-label="Conflicted files">
+22 -4
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { Archive, Download, Trash2, Upload } from "@lucide/svelte";
import { Archive, ChevronDown, ChevronRight, Download, Trash2, Upload } from "@lucide/svelte";
import type { GitStash } from "../types";
interface Props {
@@ -26,6 +26,7 @@
let message = $state("");
let includeUntracked = $state(true);
let open = $state(false);
function submitPush() {
onPush(message, includeUntracked);
@@ -37,16 +38,33 @@
}
</script>
<section class="panel stash-panel overflow-hidden" aria-label="Git stash">
<section class="panel stash-panel overflow-hidden" class:collapsed={!open} aria-label="Git stash">
<div class="section-head">
<div>
<span class="eyebrow">Stash</span>
<h2 class="mt-0.5 text-ink text-base font-bold leading-tight">Shelved changes</h2>
</div>
<span class="pill pill-count">{stashes.length}</span>
<div class="stash-head-actions">
<button
class="stash-toggle"
type="button"
onclick={() => { open = !open; }}
aria-expanded={open}
title={open ? "Collapse stash panel" : "Expand stash panel"}
>
{#if open}
<ChevronDown size={14} aria-hidden="true" />
{:else}
<ChevronRight size={14} aria-hidden="true" />
{/if}
</button>
<span class="pill pill-count">{stashes.length}</span>
</div>
</div>
{#if !hasRepository}
{#if !open}
<!-- collapsed -->
{:else if !hasRepository}
<div class="blank-state">No repository loaded.</div>
{:else}
<div class="stash-create">