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:
+76
-6
@@ -54,6 +54,9 @@
|
||||
pull,
|
||||
push,
|
||||
renameBranch,
|
||||
rebaseAbort,
|
||||
rebaseBranch,
|
||||
rebaseContinue,
|
||||
getRemoteUrl,
|
||||
credLoad,
|
||||
credSave,
|
||||
@@ -236,9 +239,12 @@
|
||||
$: unstagedCount = status?.files.filter((f) => f.unstaged !== null).length ?? 0;
|
||||
$: conflictedFiles = changedFiles.filter((f) => f.staged === "conflicted" || f.unstaged === "conflicted");
|
||||
$: hasConflicts = conflictedFiles.length > 0;
|
||||
$: canCommit = hasRepository && stagedCount > 0 && commitMessage.trim().length > 0 && !hasConflicts && !isBusy;
|
||||
$: commitBlockReason = hasConflicts
|
||||
? `${conflictedFiles.length} ${conflictedFiles.length === 1 ? "merge conflict must" : "merge conflicts must"} be resolved before committing.`
|
||||
$: rebaseInProgress = status?.rebase_in_progress ?? false;
|
||||
$: canCommit = hasRepository && stagedCount > 0 && commitMessage.trim().length > 0 && !hasConflicts && !rebaseInProgress && !isBusy;
|
||||
$: commitBlockReason = rebaseInProgress
|
||||
? "A rebase is in progress. Resolve conflicts and use Rebase continue or abort the rebase."
|
||||
: hasConflicts
|
||||
? `${conflictedFiles.length} ${conflictedFiles.length === 1 ? "conflict must" : "conflicts must"} be resolved before committing.`
|
||||
: "";
|
||||
$: canCompare = hasRepository && compareFrom.length > 0 && compareTo.length > 0 && compareFrom !== compareTo && !isBusy;
|
||||
$: localBranches = branches.filter((b) => !b.remote);
|
||||
@@ -1120,6 +1126,46 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function rebaseOnto(branch: GitBranchInfo) {
|
||||
if (!activeRepoPath || branch.current || rebaseInProgress) return;
|
||||
await runOperation(`Rebasing onto ${branch.name}`, async () => {
|
||||
applyStatus(await rebaseBranch(activeRepoPath, branch.name));
|
||||
await refreshBranchList(activeRepoPath);
|
||||
await refreshCommitHistory(activeRepoPath);
|
||||
await refreshExplorerFiles(activeRepoPath);
|
||||
await refreshFileHistory(activeRepoPath);
|
||||
});
|
||||
}
|
||||
|
||||
async function continueRebase() {
|
||||
if (!activeRepoPath || !rebaseInProgress || hasConflicts) return;
|
||||
await runOperation("Continuing rebase", async () => {
|
||||
applyStatus(await rebaseContinue(activeRepoPath));
|
||||
await refreshBranchList(activeRepoPath);
|
||||
await refreshCommitHistory(activeRepoPath);
|
||||
await refreshExplorerFiles(activeRepoPath);
|
||||
await refreshFileHistory(activeRepoPath);
|
||||
});
|
||||
}
|
||||
|
||||
async function abortRebase() {
|
||||
if (!activeRepoPath || !rebaseInProgress) return;
|
||||
const confirmed = window.confirm("Abort the current rebase and return to the previous state?");
|
||||
if (!confirmed) return;
|
||||
|
||||
await runOperation("Aborting rebase", async () => {
|
||||
applyStatus(await rebaseAbort(activeRepoPath));
|
||||
preparedResolutions = {};
|
||||
resolveDialogOpen = false;
|
||||
conflict = null;
|
||||
conflictTarget = "";
|
||||
await refreshBranchList(activeRepoPath);
|
||||
await refreshCommitHistory(activeRepoPath);
|
||||
await refreshExplorerFiles(activeRepoPath);
|
||||
await refreshFileHistory(activeRepoPath);
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve the keychain key (host/org) for the active repo's remote.
|
||||
async function currentCredKey(): Promise<string | null> {
|
||||
if (!activeRepoPath) return null;
|
||||
@@ -1519,7 +1565,11 @@
|
||||
const message = commitMessage.trim();
|
||||
if (!message || !activeRepoPath) return;
|
||||
if (hasConflicts) {
|
||||
errorMessage = "Resolve all merge conflicts before committing.";
|
||||
errorMessage = "Resolve all conflicts before committing.";
|
||||
return;
|
||||
}
|
||||
if (rebaseInProgress) {
|
||||
errorMessage = "A rebase is in progress. Use Rebase continue or abort the rebase.";
|
||||
return;
|
||||
}
|
||||
await runOperation("Committing", async () => {
|
||||
@@ -1939,14 +1989,33 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if workspaceActive && hasConflicts}
|
||||
{#if workspaceActive && hasConflicts && !rebaseInProgress}
|
||||
<section class="notice conflict" role="alert">
|
||||
<GitMerge size={17} aria-hidden="true" />
|
||||
<span>{conflictedFiles.length} {conflictedFiles.length === 1 ? "file has" : "files have"} merge conflicts.</span>
|
||||
<span>{conflictedFiles.length} {conflictedFiles.length === 1 ? "file has" : "files have"} conflicts.</span>
|
||||
<button type="button" onclick={openResolveDialog} disabled={isBusy}>Resolve conflicts</button>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if workspaceActive && rebaseInProgress}
|
||||
<section class="notice rebase" role="status">
|
||||
<GitBranch size={17} aria-hidden="true" />
|
||||
<span>
|
||||
Rebase in progress.
|
||||
{#if hasConflicts}
|
||||
Resolve conflicts, then continue.
|
||||
{:else}
|
||||
Continue when the index is ready, or abort to return to the previous state.
|
||||
{/if}
|
||||
</span>
|
||||
{#if hasConflicts}
|
||||
<button type="button" onclick={openResolveDialog} disabled={isBusy}>Resolve conflicts</button>
|
||||
{/if}
|
||||
<button type="button" onclick={continueRebase} disabled={isBusy || hasConflicts}>Continue</button>
|
||||
<button type="button" onclick={abortRebase} disabled={isBusy}>Abort</button>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeView === "management"}
|
||||
<section class="repo-management" aria-label="Repository Management">
|
||||
<div class="repo-management-head">
|
||||
@@ -2073,6 +2142,7 @@
|
||||
{isBusy}
|
||||
onCheckout={checkout}
|
||||
onMerge={merge}
|
||||
onRebase={rebaseOnto}
|
||||
onCreateBranch={createNewBranch}
|
||||
onRenameBranch={renameLocalBranch}
|
||||
onDeleteBranch={deleteLocalBranch}
|
||||
|
||||
Reference in New Issue
Block a user