From f21842eb94dce6f3c80e156424e1a23cf81a8db8 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:40:28 +0800 Subject: [PATCH 01/13] feat(windows): translate merge and rebase commands for the platform dispatcher - translate git_merge and git_rebase into git.write with qualified refs/heads references, mirroring the checkout translations - add git_integration_preflight, git_operation_state, git_operation_continue/abort/skip, and git_conflict_markers translations so the frontend can drive the shared core's conflict machinery --- windows/tauri/src-tauri/src/platform.rs | 118 ++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/windows/tauri/src-tauri/src/platform.rs b/windows/tauri/src-tauri/src/platform.rs index ea1e310dd..6551bebb2 100644 --- a/windows/tauri/src-tauri/src/platform.rs +++ b/windows/tauri/src-tauri/src/platform.rs @@ -148,6 +148,41 @@ fn translate(command: &str, args: Value) -> Result<(String, Value), String> { ); "git.checkoutPreflight" } + "git_merge" | "git_rebase" => { + let branch = take_text(&mut payload, "branchName")?; + payload.insert( + "reference".into(), + json!(local_branch_reference(&branch)), + ); + payload.insert( + "operation".into(), + json!(if command == "git_merge" { + "merge" + } else { + "rebase" + }), + ); + "git.write" + } + "git_integration_preflight" => { + let branch = take_text(&mut payload, "branchName")?; + payload.insert( + "reference".into(), + json!(local_branch_reference(&branch)), + ); + "git.integrationPreflight" + } + "git_operation_state" => "git.operationState", + "git_operation_continue" | "git_operation_abort" | "git_operation_skip" => { + let operation = match command { + "git_operation_continue" => "operationContinue", + "git_operation_abort" => "operationAbort", + _ => "operationSkip", + }; + payload.insert("operation".into(), json!(operation)); + "git.write" + } + "git_conflict_markers" => "git.conflictMarkers", "git_create_stash" => { payload.insert("operation".into(), json!("stashPush")); "git.write" @@ -500,6 +535,89 @@ mod tests { ); } + #[test] + fn translates_merge_and_rebase_to_qualified_references() { + let (merge_command, merge_payload) = translate( + "git_merge", + json!({ "repoPath": "C:/work", "branchName": "feature/demo" }), + ) + .unwrap(); + assert_eq!(merge_command, "git.write"); + assert_eq!( + merge_payload, + json!({ + "root": "C:/work", + "operation": "merge", + "reference": "refs/heads/feature/demo" + }) + ); + + let (rebase_command, rebase_payload) = translate( + "git_rebase", + json!({ "repoPath": "C:/work", "branchName": "main" }), + ) + .unwrap(); + assert_eq!(rebase_command, "git.write"); + assert_eq!( + rebase_payload, + json!({ + "root": "C:/work", + "operation": "rebase", + "reference": "refs/heads/main" + }) + ); + } + + #[test] + fn translates_integration_preflight_operation() { + let (command, payload) = translate( + "git_integration_preflight", + json!({ "repoPath": "C:/work", "branchName": "main", "operation": "rebase" }), + ) + .unwrap(); + + assert_eq!(command, "git.integrationPreflight"); + assert_eq!( + payload, + json!({ + "root": "C:/work", + "reference": "refs/heads/main", + "operation": "rebase" + }) + ); + } + + #[test] + fn translates_operation_state_and_resolution_commands() { + let (state_command, state_payload) = + translate("git_operation_state", json!({ "repoPath": "C:/work" })).unwrap(); + assert_eq!(state_command, "git.operationState"); + assert_eq!(state_payload, json!({ "root": "C:/work" })); + + for (compat, operation) in [ + ("git_operation_continue", "operationContinue"), + ("git_operation_abort", "operationAbort"), + ("git_operation_skip", "operationSkip"), + ] { + let (command, payload) = + translate(compat, json!({ "repoPath": "C:/work" })).unwrap(); + assert_eq!(command, "git.write"); + assert_eq!( + payload, + json!({ "root": "C:/work", "operation": operation }) + ); + } + } + + #[test] + fn translates_conflict_markers_request() { + let (command, payload) = + translate("git_conflict_markers", json!({ "repoPath": "C:/work" })).unwrap(); + + assert_eq!(command, "git.conflictMarkers"); + assert_eq!(payload, json!({ "root": "C:/work" })); + } + #[test] fn translates_delete_branch_to_qualified_reference() { let (command, payload) = translate( From a9a0e4e20043cf34c05d6911aa1e6d4b601629d7 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:41:40 +0800 Subject: [PATCH 02/13] feat(windows): adapt operation state and integration preflight results - map git.operationState responses into a UI-ready shape and collapse the no-operation case to null, matching the macOS model - pass through integration preflight blocking paths and conflict marker paths for the upcoming banner and commit guard --- .../core-result-adapter.operation.test.ts | 79 +++++++++++++++++++ .../tauri/src/platform/core-result-adapter.ts | 31 ++++++++ 2 files changed, 110 insertions(+) create mode 100644 windows/tauri/src/platform/core-result-adapter.operation.test.ts diff --git a/windows/tauri/src/platform/core-result-adapter.operation.test.ts b/windows/tauri/src/platform/core-result-adapter.operation.test.ts new file mode 100644 index 000000000..f58285785 --- /dev/null +++ b/windows/tauri/src/platform/core-result-adapter.operation.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, test } from "bun:test"; +import { adaptCoreResult } from "./core-result-adapter"; + +describe("git operation state adaptation", () => { + test("maps an in-progress rebase with progress counters", () => { + const state = adaptCoreResult( + "git_operation_state", + { repoPath: "C:/work" }, + { + kind: "rebase", + reference: "abc123", + step: 3, + total: 7, + conflictedPaths: ["src/main.rs", "README.md"], + }, + ); + + expect(state).toEqual({ + kind: "rebase", + reference: "abc123", + step: 3, + total: 7, + conflictedPaths: ["src/main.rs", "README.md"], + }); + }); + + test("returns null when no operation is in progress", () => { + const state = adaptCoreResult( + "git_operation_state", + { repoPath: "C:/work" }, + { kind: "", reference: null, step: null, total: null, conflictedPaths: [] }, + ); + + expect(state).toBeNull(); + }); + + test("keeps merge state without optional counters", () => { + const state = adaptCoreResult( + "git_operation_state", + { repoPath: "C:/work" }, + { kind: "merge", reference: null, step: null, total: null, conflictedPaths: ["a.txt"] }, + ); + + expect(state).toEqual({ + kind: "merge", + reference: null, + step: null, + total: null, + conflictedPaths: ["a.txt"], + }); + }); +}); + +describe("git integration preflight adaptation", () => { + test("passes through blocking paths and the blocks-entirely flag", () => { + const preflight = adaptCoreResult( + "git_integration_preflight", + { repoPath: "C:/work", branchName: "main", operation: "rebase" }, + { blockingPaths: ["a.txt", "b.txt"], blocksEntirely: true }, + ); + + expect(preflight).toEqual({ + blockingPaths: ["a.txt", "b.txt"], + blocksEntirely: true, + }); + }); +}); + +describe("git conflict marker adaptation", () => { + test("passes through staged files that still contain markers", () => { + const markers = adaptCoreResult( + "git_conflict_markers", + { repoPath: "C:/work" }, + { paths: ["src/conflicted.rs"] }, + ); + + expect(markers).toEqual({ paths: ["src/conflicted.rs"] }); + }); +}); diff --git a/windows/tauri/src/platform/core-result-adapter.ts b/windows/tauri/src/platform/core-result-adapter.ts index 016ff75fb..1b946ec83 100644 --- a/windows/tauri/src/platform/core-result-adapter.ts +++ b/windows/tauri/src/platform/core-result-adapter.ts @@ -219,6 +219,37 @@ export function adaptCoreResult( } case "git_checkout_tag": return { success: true, hasChanges: false, message: "" } as T; + case "git_operation_state": { + const kind = typeof data.kind === "string" ? data.kind : ""; + if (!kind) { + return null as T; + } + const conflictedPaths = Array.isArray(data.conflictedPaths) + ? data.conflictedPaths.map((path: unknown) => String(path)) + : []; + return { + kind, + reference: typeof data.reference === "string" ? data.reference : null, + step: typeof data.step === "number" ? data.step : null, + total: typeof data.total === "number" ? data.total : null, + conflictedPaths, + } as T; + } + case "git_integration_preflight": { + const blockingPaths = Array.isArray(data.blockingPaths) + ? data.blockingPaths.map((path: unknown) => String(path)) + : []; + return { + blockingPaths, + blocksEntirely: Boolean(data.blocksEntirely), + } as T; + } + case "git_conflict_markers": { + const paths = Array.isArray(data.paths) + ? data.paths.map((path: unknown) => String(path)) + : []; + return { paths } as T; + } default: return value as T; } From 664b2a5546caef1d2a1f46f81ed29ceab05f5cbf Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:45:52 +0800 Subject: [PATCH 03/13] feat(windows): track in-progress Git operations in the git store - add git-integration-api with preflight-first merge/rebase that distinguishes conflict stops from real failures via operation state - store operationState in the workspace git store and refresh it on every status pass so banner state stays true to the repository --- .../features/git/api/git-integration-api.ts | 142 ++++++++++++++++++ .../git/hooks/use-git-data-controller.ts | 11 +- .../src/features/git/stores/git.store.test.ts | 1 + .../src/features/git/stores/git.store.ts | 13 +- .../tauri/src/features/git/types/git.types.ts | 14 ++ 5 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 windows/tauri/src/features/git/api/git-integration-api.ts diff --git a/windows/tauri/src/features/git/api/git-integration-api.ts b/windows/tauri/src/features/git/api/git-integration-api.ts new file mode 100644 index 000000000..ba528ae4d --- /dev/null +++ b/windows/tauri/src/features/git/api/git-integration-api.ts @@ -0,0 +1,142 @@ +import { invoke as tauriInvoke } from "@/platform/tauri-core"; +import { emitGitChanged } from "../events/git-events"; +import { resolveRepositoryPathOrThrow } from "./git-repo-api"; +import type { GitOperationState } from "../types/git.types"; + +type IntegrationOperation = "merge" | "rebase"; + +interface IntegrationPreflightResult { + blockingPaths: string[]; + blocksEntirely: boolean; +} + +export type IntegrationOutcome = + | { status: "clean" } + | { status: "conflicts"; conflictedPaths: string[] } + | { status: "blocked"; blockingPaths: string[]; blocksEntirely: boolean } + | { status: "error"; message: string }; + +export interface OperationResolution { + ok: boolean; + message: string; +} + +const errorMessage = (error: unknown): string => { + const message = error instanceof Error ? error.message : String(error); + return message.trim() || "Git operation failed"; +}; + +const notifyOperationChanged = (repoPath: string, source: string) => { + emitGitChanged({ + repoPath, + scopes: ["working-tree", "history", "refs"], + source, + }); +}; + +export const getOperationState = async ( + repoPath: string, +): Promise => { + try { + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + return await tauriInvoke("git_operation_state", { + repoPath: resolvedRepoPath, + }); + } catch { + return null; + } +}; + +export const getConflictMarkerPaths = async (repoPath: string): Promise => { + try { + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + const result = await tauriInvoke<{ paths: string[] }>("git_conflict_markers", { + repoPath: resolvedRepoPath, + }); + return result.paths; + } catch { + return []; + } +}; + +const runIntegration = async ( + repoPath: string, + branchName: string, + operation: IntegrationOperation, +): Promise => { + const command = operation === "merge" ? "git_merge" : "git_rebase"; + try { + await tauriInvoke(command, { repoPath, branchName }); + notifyOperationChanged(repoPath, `${operation}-completed`); + return { status: "clean" }; + } catch (error) { + // A conflict stop exits non-zero like a real failure; the authoritative + // distinction is whether Git left an operation state behind. + const state = await getOperationState(repoPath); + if (state && state.kind === operation && state.conflictedPaths.length > 0) { + notifyOperationChanged(repoPath, `${operation}-conflicts`); + return { status: "conflicts", conflictedPaths: state.conflictedPaths }; + } + return { status: "error", message: errorMessage(error) }; + } +}; + +const startIntegration = async ( + repoPath: string, + branchName: string, + operation: IntegrationOperation, +): Promise => { + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + + let preflight: IntegrationPreflightResult | null = null; + try { + preflight = await tauriInvoke( + "git_integration_preflight", + { repoPath: resolvedRepoPath, branchName, operation }, + ); + } catch { + // A failed preflight must not block the operation itself; Git will still + // refuse safely when the tree is dirty. + } + + if (preflight && preflight.blockingPaths.length > 0) { + return { + status: "blocked", + blockingPaths: preflight.blockingPaths, + blocksEntirely: preflight.blocksEntirely, + }; + } + + return runIntegration(resolvedRepoPath, branchName, operation); +}; + +export const mergeBranch = (repoPath: string, branchName: string) => + startIntegration(repoPath, branchName, "merge"); + +export const rebaseOntoBranch = (repoPath: string, branchName: string) => + startIntegration(repoPath, branchName, "rebase"); + +const resolveOperation = async ( + repoPath: string, + action: "git_operation_continue" | "git_operation_abort" | "git_operation_skip", +): Promise => { + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + try { + await tauriInvoke(action, { repoPath: resolvedRepoPath }); + notifyOperationChanged(resolvedRepoPath, "operation-resolved"); + return { ok: true, message: "Git operation finished" }; + } catch (error) { + // Even a rejected continue can change repository state, so refresh anyway. + notifyOperationChanged(resolvedRepoPath, "operation-resolution-rejected"); + return { ok: false, message: errorMessage(error) }; + } +}; + +export const continueOperation = (repoPath: string) => + resolveOperation(repoPath, "git_operation_continue"); + +export const abortOperation = (repoPath: string) => + resolveOperation(repoPath, "git_operation_abort"); + +export const skipOperationStep = (repoPath: string) => + resolveOperation(repoPath, "git_operation_skip"); diff --git a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts index 1966e7d2a..71b750040 100644 --- a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts +++ b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef } from "react"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import { getBranches } from "../api/git-branches-api"; import { getGitHistory } from "../api/git-commits-api"; +import { getOperationState } from "../api/git-integration-api"; import { getStashes } from "../api/git-stash-api"; import { getGitStatus } from "../api/git-status-api"; import { @@ -40,11 +41,12 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl gitActions.setIsLoadingGitData(true); try { - const [status, history, branches, stashes] = await Promise.all([ + const [status, history, branches, stashes, operationState] = await Promise.all([ getGitStatus(repoPath), getGitHistory(repoPath, 50), getBranches(repoPath), getStashes(repoPath), + getOperationState(repoPath), ]); if ( @@ -60,6 +62,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl hasMoreCommits: history?.hasMore ?? false, branches, stashes, + operationState, repoPath, }); } catch (error) { @@ -91,11 +94,14 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl refreshAll || scopes.includes("refs") || scopes.includes("repository"); const shouldRefreshStashes = refreshAll || scopes.includes("stashes") || scopes.includes("repository"); - const [status, branches, stashes, history] = await Promise.all([ + const [status, branches, stashes, history, operationState] = await Promise.all([ getGitStatus(repoPath), shouldRefreshRefs ? getBranches(repoPath) : Promise.resolve(undefined), shouldRefreshStashes ? getStashes(repoPath) : Promise.resolve(undefined), shouldRefreshHistory ? getGitHistory(repoPath, 50) : Promise.resolve(undefined), + // Operation state rides along on every refresh: staging a file or + // an external Git command can end a conflict at any moment. + getOperationState(repoPath), ]); if ( @@ -110,6 +116,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl branches, commits: history?.commits, hasMoreCommits: history?.hasMore, + operationState, repoPath, }); diff --git a/windows/tauri/src/features/git/stores/git.store.test.ts b/windows/tauri/src/features/git/stores/git.store.test.ts index 70d18e330..e625b746d 100644 --- a/windows/tauri/src/features/git/stores/git.store.test.ts +++ b/windows/tauri/src/features/git/stores/git.store.test.ts @@ -31,6 +31,7 @@ const loadInitialHistory = ( hasMoreCommits: true, branches: [], stashes: [], + operationState: null, repoPath, }); }; diff --git a/windows/tauri/src/features/git/stores/git.store.ts b/windows/tauri/src/features/git/stores/git.store.ts index 7223fd477..0c88d6c6b 100644 --- a/windows/tauri/src/features/git/stores/git.store.ts +++ b/windows/tauri/src/features/git/stores/git.store.ts @@ -2,7 +2,7 @@ import { createStore } from "zustand/vanilla"; import { createWorkspaceScopedStore } from "@/features/workspace/stores/create-workspace-scoped-store"; import { getGitHistory } from "../api/git-commits-api"; import { getGitStatus } from "../api/git-status-api"; -import type { GitCommit, GitStash, GitStatus } from "../types/git.types"; +import type { GitCommit, GitOperationState, GitStash, GitStatus } from "../types/git.types"; interface GitState { gitStatus: GitStatus | null; @@ -10,6 +10,7 @@ interface GitState { commits: GitCommit[]; branches: string[]; stashes: GitStash[]; + operationState: GitOperationState | null; hasMoreCommits: boolean; isLoadingMoreCommits: boolean; isLoadingGitData: boolean; @@ -26,6 +27,7 @@ interface GitState { hasMoreCommits: boolean; branches: string[]; stashes: GitStash[]; + operationState: GitOperationState | null; repoPath: string; }) => void; refreshGitData: (data: { @@ -33,6 +35,7 @@ interface GitState { branches?: string[]; commits?: GitCommit[]; hasMoreCommits?: boolean; + operationState: GitOperationState | null; repoPath: string; }) => void; refreshWorkspaceGitStatus: (repoPath: string) => Promise; @@ -58,6 +61,7 @@ export const createGitStore = () => commits: [], branches: [], stashes: [], + operationState: null, hasMoreCommits: true, isLoadingMoreCommits: false, isLoadingGitData: false, @@ -76,6 +80,7 @@ export const createGitStore = () => commits: [], branches: [], stashes: [], + operationState: null, hasMoreCommits: true, isLoadingMoreCommits: false, currentRepoPath: repoPath, @@ -88,6 +93,7 @@ export const createGitStore = () => hasMoreCommits, branches, stashes, + operationState, repoPath, }) => { if (get().currentRepoPath !== repoPath) { @@ -99,18 +105,20 @@ export const createGitStore = () => commits, branches, stashes, + operationState, hasMoreCommits, currentRepoPath: repoPath, }); }, - refreshGitData: ({ gitStatus, branches, commits, hasMoreCommits, repoPath }) => { + refreshGitData: ({ gitStatus, branches, commits, hasMoreCommits, operationState, repoPath }) => { if (get().currentRepoPath !== repoPath) { return; } set({ gitStatus, + operationState, ...(branches ? { branches } : {}), ...(commits ? { @@ -183,6 +191,7 @@ export const createGitStore = () => commits: [], branches: [], stashes: [], + operationState: null, hasMoreCommits: true, isLoadingMoreCommits: false, isLoadingGitData: false, diff --git a/windows/tauri/src/features/git/types/git.types.ts b/windows/tauri/src/features/git/types/git.types.ts index d9646703b..e19ede84f 100644 --- a/windows/tauri/src/features/git/types/git.types.ts +++ b/windows/tauri/src/features/git/types/git.types.ts @@ -116,3 +116,17 @@ export interface GitBlameLine { time: number; commit: string; } + +export type GitOperationKind = "merge" | "rebase" | "cherryPick" | "revert"; + +/** + * An in-progress merge/rebase/cherry-pick/revert detected from the repository's + * Git marker files, so operations started outside the app are reported too. + */ +export interface GitOperationState { + kind: GitOperationKind; + reference: string | null; + step: number | null; + total: number | null; + conflictedPaths: string[]; +} From be4d1d6c73915be7c9565fbc96c3a5406f8c24f4 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:49:52 +0800 Subject: [PATCH 04/13] feat(windows): add the Git operation banner above the changes list - mirror the macOS banner: in-progress title, rebase step counters, conflict count, and Continue/Skip/Abort actions - keep Continue disabled while conflicts remain and re-run resolution through the shared core, refreshing state after every attempt --- .../git/components/git-operation-banner.tsx | 132 ++++++++++++++++++ .../src/features/git/components/git-view.tsx | 34 +++-- 2 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 windows/tauri/src/features/git/components/git-operation-banner.tsx diff --git a/windows/tauri/src/features/git/components/git-operation-banner.tsx b/windows/tauri/src/features/git/components/git-operation-banner.tsx new file mode 100644 index 000000000..6ecfbcfc0 --- /dev/null +++ b/windows/tauri/src/features/git/components/git-operation-banner.tsx @@ -0,0 +1,132 @@ +import { useCallback, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/ui/button"; +import { WarningIcon } from "@/ui/icons"; +import { + abortOperation, + continueOperation, + skipOperationStep, + type OperationResolution, +} from "../api/git-integration-api"; +import { useGitStore } from "../stores/git.store"; +import { cn } from "@/utils/cn"; +import type { GitOperationKind } from "../types/git.types"; + +const IN_PROGRESS_TITLES: Record = { + merge: "Merge in progress", + rebase: "Rebase in progress", + cherryPick: "Cherry-pick in progress", + revert: "Revert in progress", +}; + +const CONTINUE_TITLES: Record = { + merge: "Continue Merge", + rebase: "Continue Rebase", + cherryPick: "Continue Cherry-pick", + revert: "Continue Revert", +}; + +interface GitOperationBannerProps { + repoPath: string; +} + +/** + * Pins the state of an in-progress merge/rebase/cherry-pick/revert above the + * changes list. Deliberately not a dialog: the controls must stay reachable + * while the user edits conflicted files, mirroring the macOS sidebar banner. + */ +const GitOperationBanner = ({ repoPath }: GitOperationBannerProps) => { + const operation = useGitStore((state) => state.operationState); + + const [isResolving, setIsResolving] = useState(false); + + const resolve = useCallback( + async (action: (repoPath: string) => Promise) => { + setIsResolving(true); + try { + const result = await action(repoPath); + // A rejected continue can still change state; the API already + // requested a refresh either way, so only surface the message here. + if (!result.ok) { + toast.error(result.message); + } + } catch (error) { + toast.error(error instanceof Error ? error.message : "Git operation failed"); + } finally { + setIsResolving(false); + } + }, + [repoPath, toast], + ); + + if (!operation) { + return null; + } + + const hasConflicts = operation.conflictedPaths.length > 0; + const hasProgress = operation.step != null && operation.total != null; + + return ( +
+
+ + {IN_PROGRESS_TITLES[operation.kind]} + {operation.reference ? ( + + — {operation.reference.slice(0, 7)} + + ) : null} +
+ +
+ {hasProgress ? Step {operation.step} of {operation.total}. : null} + {hasConflicts ? ( + + Resolve {operation.conflictedPaths.length} conflicted file + {operation.conflictedPaths.length === 1 ? "" : "s"}, stage them, then continue. + + ) : ( + All conflicts resolved. Continue to finish, or abort to undo. + )} +
+ +
+ + {operation.kind === "rebase" ? ( + + ) : null} + +
+
+ ); +}; + +export default GitOperationBanner; diff --git a/windows/tauri/src/features/git/components/git-view.tsx b/windows/tauri/src/features/git/components/git-view.tsx index d70012b27..c8993cbf0 100644 --- a/windows/tauri/src/features/git/components/git-view.tsx +++ b/windows/tauri/src/features/git/components/git-view.tsx @@ -57,6 +57,7 @@ import GitCommitPanel from "./git-commit-panel"; import GitCommandSurface from "./git-command-surface"; import GitRemoteManager from "./git-remote-manager"; import GitTagManager from "./git-tag-manager"; +import GitOperationBanner from "./git-operation-banner"; import GitStatusPanel from "./status/git-status-panel"; interface GitViewProps { @@ -834,21 +835,24 @@ const GitView = ({ repoPath, onFileSelect, isActive }: GitViewProps) => { { id: "changes", content: ( - void handleViewWorkingTreeDiff(scope)} - onShowCommitDiffPicker={handleShowCommitDiffList} - onShowBranchDiffPicker={() => void handleShowBranchDiffList()} - onShowStashDiffPicker={() => { - setShowStashList(true); - setStashSearchQuery(""); - }} - onRefresh={refreshAfterAction} - repoPath={activeRepoPath} - /> +
+ + void handleViewWorkingTreeDiff(scope)} + onShowCommitDiffPicker={handleShowCommitDiffList} + onShowBranchDiffPicker={() => void handleShowBranchDiffList()} + onShowStashDiffPicker={() => { + setShowStashList(true); + setStashSearchQuery(""); + }} + onRefresh={refreshAfterAction} + repoPath={activeRepoPath} + /> +
), }, { From a81920666ad5a3770803f70c37cee0e0f5e2440b Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:53:42 +0800 Subject: [PATCH 05/13] feat(windows): start merges and rebases from the branch manager - add a per-branch action menu with Merge into Current Branch and Rebase Current Branch onto This behind one confirmation each - surface clean, conflict-stop, blocked-by-dirty, and failure outcomes with the shared core's preflight and operation state --- .../git/components/git-branch-manager.tsx | 129 +++++++++++++++--- 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/windows/tauri/src/features/git/components/git-branch-manager.tsx b/windows/tauri/src/features/git/components/git-branch-manager.tsx index fcc2b813e..928ad1727 100644 --- a/windows/tauri/src/features/git/components/git-branch-manager.tsx +++ b/windows/tauri/src/features/git/components/git-branch-manager.tsx @@ -19,12 +19,20 @@ import { CommandTabs, useCommandListNavigation, } from "@/ui/command"; -import { GitBranchIcon, FolderOpenIcon, NodesIcon } from "@/ui/icons"; +import { GitBranchIcon, FolderOpenIcon, GitMergeIcon, NodesIcon, DotsThreeIcon } from "@/ui/icons"; import { showConfirmDialog } from "@/ui/dialog"; import { cn } from "@/utils/cn"; import { getFolderName, getRelativePath } from "@/utils/path-helpers"; import { matchesSearchQuery } from "@/utils/search-match"; import { checkoutBranch, createBranch, deleteBranch, getBranches } from "../api/git-branches-api"; +import { mergeBranch, rebaseOntoBranch, type IntegrationOutcome } from "../api/git-integration-api"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/ui/dropdown-menu"; import { resolveRepositoryPath } from "../api/git-repo-api"; import { createStash } from "../api/git-stash-api"; import { addWorktree, getWorktrees } from "../api/git-worktrees-api"; @@ -343,6 +351,66 @@ const GitBranchManager = ({ } }; + const reportIntegrationOutcome = (branchName: string, outcome: IntegrationOutcome) => { + if (outcome.status === "clean") { + showToast({ message: `Merged ${branchName} successfully.`, type: "success" }); + } else if (outcome.status === "conflicts") { + showToast({ + message: `Stopped with ${outcome.conflictedPaths.length} conflicted file${ + outcome.conflictedPaths.length === 1 ? "" : "s" + }. Resolve them in the changes list, then continue.`, + type: "warning", + duration: 6000, + }); + } else if (outcome.status === "blocked") { + const listed = outcome.blockingPaths.slice(0, 3).join(", "); + const remaining = outcome.blockingPaths.length - Math.min(outcome.blockingPaths.length, 3); + showToast({ + message: `Uncommitted changes would be overwritten: ${listed}${ + remaining > 0 ? ` (+${remaining} more)` + : ""}. Stash or commit them first.`, + type: "warning", + duration: 6000, + }); + } else { + showToast({ message: outcome.message, type: "error" }); + } + }; + + const handleIntegration = async (branchName: string, operation: "merge" | "rebase") => { + if (!repoPath || !branchName || branchName === currentBranch) return; + + const action = operation === "merge" ? "Merge" : "Rebase"; + const message = + operation === "merge" + ? `Merge branch "${branchName}" into "${currentBranch}"? Conflicts may require resolution.` + : `Rebase "${currentBranch}" onto "${branchName}"? Conflicts may require resolution.`; + const confirmed = await showConfirmDialog(message, { + title: `${action} Branch`, + confirmLabel: action, + }); + if (!confirmed) return; + + setIsLoading(true); + try { + const outcome = + operation === "merge" + ? await mergeBranch(repoPath, branchName) + : await rebaseOntoBranch(repoPath, branchName); + reportIntegrationOutcome(branchName, outcome); + if (outcome.status === "clean" || outcome.status === "conflicts") { + onBranchChange?.(); + } + } catch (error) { + showToast({ + message: error instanceof Error ? error.message : `${action} failed.`, + type: "error", + }); + } finally { + setIsLoading(false); + } + }; + const handleCreateBranch = async (branchName: string) => { if (!repoPath || !branchName.trim()) return; @@ -623,6 +691,8 @@ const GitBranchManager = ({ onMouseEnter={() => setSelectedIndex(index + (createBranchName ? 1 : 0))} onSelect={() => void handleBranchChange(branch)} onDelete={() => void handleDeleteBranch(branch)} + onMerge={() => void handleIntegration(branch, "merge")} + onRebase={() => void handleIntegration(branch, "rebase")} /> ))} @@ -772,6 +842,8 @@ function BranchRow({ onMouseEnter, onSelect, onDelete, + onMerge, + onRebase, }: { branch: string; isCurrent: boolean; @@ -780,6 +852,8 @@ function BranchRow({ onMouseEnter: () => void; onSelect: () => void; onDelete: () => void; + onMerge: () => void; + onRebase: () => void; }) { return ( current : null} action={ !isCurrent ? ( - + + + } + > + + + + + + Merge into Current Branch + + + + Rebase Current Branch onto This + + + + + Delete Branch + + + + ) : null } /> From 8cec913d7b9614dc1dbcee9b580af90255c6304b Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 14:55:03 +0800 Subject: [PATCH 06/13] feat(windows): guard commits while conflicts stay unresolved - refuse to commit while the operation state still lists conflicted paths or staged files contain conflict markers, with the file names surfaced instead of Git's raw refusal --- .../features/git/components/git-commit-panel.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/windows/tauri/src/features/git/components/git-commit-panel.tsx b/windows/tauri/src/features/git/components/git-commit-panel.tsx index 2a4b51189..089c5fbec 100644 --- a/windows/tauri/src/features/git/components/git-commit-panel.tsx +++ b/windows/tauri/src/features/git/components/git-commit-panel.tsx @@ -24,8 +24,10 @@ import { } from "@/features/editor/services/editor-inline-edit-service"; import { getFileDiff } from "../api/git-diff-api"; import { commitChanges, getGitLog } from "../api/git-commits-api"; +import { getConflictMarkerPaths } from "../api/git-integration-api"; import { pullChanges, pushChanges, type GitRemoteActionResult } from "../api/git-remotes-api"; import { useGitBlameStore } from "../stores/git-blame.store"; +import { useGitStore } from "../stores/git.store"; import type { GitDiff, GitFile } from "../types/git.types"; interface GitCommitPanelProps { @@ -287,6 +289,20 @@ const GitCommitPanel = ({ const handleCommit = async () => { if (!repoPath || !commitMessage.trim() || stagedFilesCount === 0) return; + // A conflicted merge/rebase must be resolved before the merge commit can + // be finalized; guard here so Git's raw refusal never reaches the user. + const conflictedPaths = useGitStore.getState().operationState?.conflictedPaths ?? []; + if (conflictedPaths.length > 0) { + setError(`Resolve the conflicts first: ${conflictedPaths.join(", ")}`); + return; + } + + const markerPaths = await getConflictMarkerPaths(repoPath); + if (markerPaths.length > 0) { + setError(`Conflict markers remain in: ${markerPaths.join(", ")}`); + return; + } + setIsCommitting(true); setError(null); From b725af1861f388808d9865788c9c07c9ddf7b8ce Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 15:30:37 +0800 Subject: [PATCH 07/13] fix(windows): stop the branch row click hijacking the action menu The action wrapper only stopped pointer-down propagation, so the click that opened the menu also bubbled to the row's checkout handler, which disabled the trigger and closed the menu instantly. Stop click propagation like the retired delete button did. --- windows/tauri/src/features/git/components/git-branch-manager.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/tauri/src/features/git/components/git-branch-manager.tsx b/windows/tauri/src/features/git/components/git-branch-manager.tsx index 928ad1727..c945ed451 100644 --- a/windows/tauri/src/features/git/components/git-branch-manager.tsx +++ b/windows/tauri/src/features/git/components/git-branch-manager.tsx @@ -883,6 +883,7 @@ function BranchRow({ event.preventDefault(); event.stopPropagation(); }} + onClick={(event) => event.stopPropagation()} > Date: Sun, 16 Aug 2026 15:37:51 +0800 Subject: [PATCH 08/13] fix(windows): drop the tooltip wrapper that killed the branch action menu Button wraps itself in a Tooltip component when given a tooltip prop, so Base UI's render prop attached the menu trigger handlers to the tooltip wrapper instead of the real button and clicks never opened the menu. Pass only aria-label, like the working diff header trigger. --- windows/tauri/src/features/git/components/git-branch-manager.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/windows/tauri/src/features/git/components/git-branch-manager.tsx b/windows/tauri/src/features/git/components/git-branch-manager.tsx index c945ed451..c8eb2be66 100644 --- a/windows/tauri/src/features/git/components/git-branch-manager.tsx +++ b/windows/tauri/src/features/git/components/git-branch-manager.tsx @@ -892,7 +892,6 @@ function BranchRow({ variant="ghost" size="icon-xs" disabled={isLoading} - tooltip={`Actions for ${branch}`} aria-label={`Branch actions for ${branch}`} type="button" /> From a21118698530f081bea2e4aa07620b35baf38924 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 16:18:32 +0800 Subject: [PATCH 09/13] fix(windows): make the branch action menu usable --- .../tauri/src/features/git/components/git-branch-manager.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/windows/tauri/src/features/git/components/git-branch-manager.tsx b/windows/tauri/src/features/git/components/git-branch-manager.tsx index c8eb2be66..9812fc05a 100644 --- a/windows/tauri/src/features/git/components/git-branch-manager.tsx +++ b/windows/tauri/src/features/git/components/git-branch-manager.tsx @@ -32,7 +32,7 @@ import { DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/ui/dropdown-menu"; +} from "@/ui/dropdown"; import { resolveRepositoryPath } from "../api/git-repo-api"; import { createStash } from "../api/git-stash-api"; import { addWorktree, getWorktrees } from "../api/git-worktrees-api"; @@ -880,7 +880,6 @@ function BranchRow({
{ - event.preventDefault(); event.stopPropagation(); }} onClick={(event) => event.stopPropagation()} @@ -899,7 +898,7 @@ function BranchRow({ > - + Merge into Current Branch From f6803ce9a42f47ba6082161749a8089c647d2a83 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 16:28:23 +0800 Subject: [PATCH 10/13] feat(windows): add Git Log tool window --- .../components/command-palette.tsx | 2 + .../command-palette/constants/git-actions.tsx | 16 ++ .../src/features/git/api/git-commits-api.ts | 29 +- .../components/log/git-commit-file-tree.tsx | 173 ++++++++++++ .../components/log/git-commit-inspector.tsx | 140 ++++++++++ .../git/components/log/git-commit-table.tsx | 258 ++++++++++++++++++ .../git/components/log/git-graph-row.tsx | 101 +++++++ .../git/components/log/git-log-title-bar.tsx | 107 ++++++++ .../components/log/git-log-tool-window.tsx | 193 +++++++++++++ .../git/components/log/git-reference-tree.tsx | 190 +++++++++++++ .../git/hooks/use-git-data-controller.ts | 7 +- .../git/hooks/use-git-log-controller.ts | 115 ++++++++ .../stores/git-log-preferences.store.test.ts | 34 +++ .../git/stores/git-log-preferences.store.ts | 86 ++++++ .../src/features/git/stores/git.store.test.ts | 9 +- .../tauri/src/features/git/types/git.types.ts | 19 ++ .../git/utils/git-graph-layout.test.ts | 57 ++++ .../features/git/utils/git-graph-layout.ts | 150 ++++++++++ .../features/git/utils/git-log-filter.test.ts | 30 ++ .../src/features/git/utils/git-log-filter.ts | 19 ++ .../git/utils/git-reference-tree.test.ts | 24 ++ .../features/git/utils/git-reference-tree.ts | 49 ++++ .../components/bottom-pane/bottom-pane.tsx | 7 + .../stores/ui-state/types/ui-state.types.ts | 3 +- .../core-result-adapter.history.test.ts | 25 ++ .../tauri/src/platform/core-result-adapter.ts | 12 + 26 files changed, 1847 insertions(+), 8 deletions(-) create mode 100644 windows/tauri/src/features/git/components/log/git-commit-file-tree.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-commit-inspector.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-commit-table.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-graph-row.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-log-title-bar.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-log-tool-window.tsx create mode 100644 windows/tauri/src/features/git/components/log/git-reference-tree.tsx create mode 100644 windows/tauri/src/features/git/hooks/use-git-log-controller.ts create mode 100644 windows/tauri/src/features/git/stores/git-log-preferences.store.test.ts create mode 100644 windows/tauri/src/features/git/stores/git-log-preferences.store.ts create mode 100644 windows/tauri/src/features/git/utils/git-graph-layout.test.ts create mode 100644 windows/tauri/src/features/git/utils/git-graph-layout.ts create mode 100644 windows/tauri/src/features/git/utils/git-log-filter.test.ts create mode 100644 windows/tauri/src/features/git/utils/git-log-filter.ts create mode 100644 windows/tauri/src/features/git/utils/git-reference-tree.test.ts create mode 100644 windows/tauri/src/features/git/utils/git-reference-tree.ts diff --git a/windows/tauri/src/features/command-palette/components/command-palette.tsx b/windows/tauri/src/features/command-palette/components/command-palette.tsx index e52686b33..4078e3993 100644 --- a/windows/tauri/src/features/command-palette/components/command-palette.tsx +++ b/windows/tauri/src/features/command-palette/components/command-palette.tsx @@ -312,6 +312,8 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont activeRepoPath, setIsSidebarVisible, setActiveView, + setIsBottomPaneVisible, + setBottomPaneActiveTab, showToast, gitOperations: { stageAllFiles, diff --git a/windows/tauri/src/features/command-palette/constants/git-actions.tsx b/windows/tauri/src/features/command-palette/constants/git-actions.tsx index eb3036e29..af519e92c 100644 --- a/windows/tauri/src/features/command-palette/constants/git-actions.tsx +++ b/windows/tauri/src/features/command-palette/constants/git-actions.tsx @@ -19,6 +19,8 @@ interface GitActionsParams { activeRepoPath?: string | null; setIsSidebarVisible: (v: boolean) => void; setActiveView: (view: "files" | "git" | "github-prs") => void; + setIsBottomPaneVisible: (visible: boolean) => void; + setBottomPaneActiveTab: (tab: "gitLog") => void; showToast: (params: { message: string; type: "success" | "error" | "info" }) => void; gitOperations: { stageAllFiles: (path: string) => Promise; @@ -38,6 +40,8 @@ export const createGitActions = (params: GitActionsParams): Action[] => { activeRepoPath, setIsSidebarVisible, setActiveView, + setIsBottomPaneVisible, + setBottomPaneActiveTab, showToast, gitOperations, onClose, @@ -155,6 +159,18 @@ export const createGitActions = (params: GitActionsParams): Action[] => { category: "Git", action: () => openGitAction({ type: "show-tab", tab: "history" }), }, + { + id: "git-open-log", + label: "Git: Open Log", + description: "Open the full Git Log tool window", + icon: , + category: "Git", + action: () => { + setBottomPaneActiveTab("gitLog"); + setIsBottomPaneVisible(true); + onClose(); + }, + }, { id: "git-manage-remotes", label: "Git: Manage Remotes", diff --git a/windows/tauri/src/features/git/api/git-commits-api.ts b/windows/tauri/src/features/git/api/git-commits-api.ts index 29c565dc9..b9d3f3982 100644 --- a/windows/tauri/src/features/git/api/git-commits-api.ts +++ b/windows/tauri/src/features/git/api/git-commits-api.ts @@ -1,5 +1,5 @@ import { invoke as tauriInvoke } from "@/platform/tauri-core"; -import type { GitCommit, GitHistorySnapshot } from "../types/git.types"; +import type { GitCommit, GitCommitFile, GitHistorySnapshot } from "../types/git.types"; import { emitGitChanged } from "../events/git-events"; import { runGitRead } from "../runtime/git-read-coordinator"; import { @@ -27,6 +27,7 @@ export const commitChanges = async (repoPath: string, message: string): Promise< export const getGitHistory = async ( repoPath: string, limit = 50, + reference?: string, ): Promise => { try { const resolvedRepoPath = await resolveRepositoryPath(repoPath); @@ -34,10 +35,11 @@ export const getGitHistory = async ( return null; } - return await runGitRead(resolvedRepoPath, `log:${limit}`, () => + return await runGitRead(resolvedRepoPath, `log:${reference ?? "all"}:${limit}`, () => tauriInvoke("git_log", { repoPath: resolvedRepoPath, limit, + ...(reference ? { reference } : {}), }), ); } catch (error) { @@ -50,3 +52,26 @@ export const getGitHistory = async ( export const getGitLog = async (repoPath: string, limit = 50): Promise => (await getGitHistory(repoPath, limit))?.commits ?? []; + +export const getCommitFiles = async ( + repoPath: string, + commitHash: string, +): Promise => { + try { + const resolvedRepoPath = await resolveRepositoryPath(repoPath); + if (!resolvedRepoPath) return null; + + const result = await runGitRead(resolvedRepoPath, `commit-files:${commitHash}`, () => + tauriInvoke<{ files: GitCommitFile[] }>("git.commitFiles", { + repoPath: resolvedRepoPath, + commit: commitHash, + }), + ); + return result.files ?? []; + } catch (error) { + if (!isNotGitRepositoryError(error)) { + console.error("Failed to get files for commit:", error); + } + return null; + } +}; diff --git a/windows/tauri/src/features/git/components/log/git-commit-file-tree.tsx b/windows/tauri/src/features/git/components/log/git-commit-file-tree.tsx new file mode 100644 index 000000000..3213138f4 --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-commit-file-tree.tsx @@ -0,0 +1,173 @@ +import { CaretDownIcon, CaretRightIcon, FileIcon, FolderIcon } from "@/ui/icons"; +import { useMemo, useState } from "react"; +import { cn } from "@/utils/cn"; +import type { GitCommitFile } from "../../types/git.types"; + +interface FileTreeNode { + id: string; + name: string; + path: string; + file?: GitCommitFile; + children: FileTreeNode[]; +} + +interface MutableFileTreeNode extends FileTreeNode { + children: MutableFileTreeNode[]; +} + +function buildFileTree(files: GitCommitFile[]): FileTreeNode[] { + const roots: MutableFileTreeNode[] = []; + for (const file of files) { + const parts = file.path.split("/").filter(Boolean); + let children = roots; + let path = ""; + parts.forEach((name, index) => { + path = path ? `${path}/${name}` : name; + let node = children.find((candidate) => candidate.name === name); + if (!node) { + node = { id: path, name, path, children: [] }; + children.push(node); + } + if (index === parts.length - 1) node.file = file; + children = node.children; + }); + } + const sort = (nodes: MutableFileTreeNode[]) => { + nodes.sort((left, right) => { + if (Boolean(left.children.length) !== Boolean(right.children.length)) { + return left.children.length ? -1 : 1; + } + return left.name.localeCompare(right.name); + }); + nodes.forEach((node) => sort(node.children)); + }; + sort(roots); + return roots; +} + +function statusClassName(status: string) { + if (status.startsWith("A")) return "text-emerald-400"; + if (status.startsWith("D")) return "text-red-400"; + if (status.startsWith("R")) return "text-amber-400"; + return "text-sky-400"; +} + +function FileNode({ + node, + depth, + collapsed, + selectedPath, + onToggle, + onSelect, + onOpen, +}: { + node: FileTreeNode; + depth: number; + collapsed: Set; + selectedPath: string | null; + onToggle: (path: string) => void; + onSelect: (path: string) => void; + onOpen: (path: string) => void; +}) { + const isDirectory = node.children.length > 0 && !node.file; + const isCollapsed = collapsed.has(node.path); + + return ( + <> + + {!isCollapsed && + node.children.map((child) => ( + + ))} + + ); +} + +export function GitCommitFileTree({ + files, + selectedPath, + onSelect, + onOpen, +}: { + files: GitCommitFile[]; + selectedPath: string | null; + onSelect: (path: string) => void; + onOpen: (path: string) => void; +}) { + const tree = useMemo(() => buildFileTree(files), [files]); + const [collapsed, setCollapsed] = useState>(new Set()); + const toggle = (path: string) => { + setCollapsed((current) => { + const next = new Set(current); + if (next.has(path)) next.delete(path); + else next.add(path); + return next; + }); + }; + + return ( +
+ {tree.map((node) => ( + + ))} +
+ ); +} diff --git a/windows/tauri/src/features/git/components/log/git-commit-inspector.tsx b/windows/tauri/src/features/git/components/log/git-commit-inspector.tsx new file mode 100644 index 000000000..35a2a728b --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-commit-inspector.tsx @@ -0,0 +1,140 @@ +import { useEffect, useRef, useState } from "react"; +import { GitDiffIcon } from "@/ui/icons"; +import { Button } from "@/ui/button"; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui/resizable"; +import { getCommitFiles } from "../../api/git-commits-api"; +import { useGitLogPreferencesStore } from "../../stores/git-log-preferences.store"; +import type { GitCommit, GitCommitFile } from "../../types/git.types"; +import { GitCommitFileTree } from "./git-commit-file-tree"; + +type FilesLoadState = "idle" | "loading" | "ready" | "failed"; + +export function GitCommitInspector({ + repoPath, + commit, + onOpenDiff, +}: { + repoPath: string | null; + commit: GitCommit | null; + onOpenDiff: (commit: GitCommit, filePath?: string) => void; +}) { + const [files, setFiles] = useState([]); + const [loadState, setLoadState] = useState("idle"); + const [selectedPath, setSelectedPath] = useState(null); + const requestIdRef = useRef(0); + const inspectorPanelLayout = useGitLogPreferencesStore.use.inspectorPanelLayout(); + const { setInspectorPanelLayout } = useGitLogPreferencesStore.use.actions(); + + useEffect(() => { + const requestId = ++requestIdRef.current; + setFiles([]); + setSelectedPath(null); + if (!repoPath || !commit) { + setLoadState("idle"); + return; + } + + setLoadState("loading"); + void getCommitFiles(repoPath, commit.hash).then((result) => { + if (requestId !== requestIdRef.current) return; + if (!result) { + setLoadState("failed"); + return; + } + setFiles(result); + setLoadState("ready"); + }); + + return () => { + requestIdRef.current += 1; + }; + }, [commit, repoPath]); + + return ( +
+ { + if (meta.isUserInteraction) setInspectorPanelLayout(layout); + }} + > + +
+
+ Commit files + + {loadState === "loading" ? "Loading…" : `${files.length} files`} + + +
+ {!commit ? ( +
+ Select a commit +
+ ) : loadState === "loading" ? ( +
+ Loading changed files… +
+ ) : loadState === "failed" ? ( +
+ Unable to load changed files +
+ ) : files.length === 0 ? ( +
+ No changed files +
+ ) : ( + onOpenDiff(commit, path)} + /> + )} +
+
+ + +
+ {commit ? ( +
+
{commit.message}
+ {commit.description ? ( +
+ {commit.description} +
+ ) : null} +
+ {commit.shortHash} · {commit.author} + {commit.email ? ` <${commit.email}>` : ""} +
+
{commit.date}
+ {commit.decorations ? ( +
{commit.decorations}
+ ) : null} +
+ {commit.hash} +
+
+ ) : ( +
+ Commit details +
+ )} +
+
+
+
+ ); +} diff --git a/windows/tauri/src/features/git/components/log/git-commit-table.tsx b/windows/tauri/src/features/git/components/log/git-commit-table.tsx new file mode 100644 index 000000000..4681f96df --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-commit-table.tsx @@ -0,0 +1,258 @@ +import { useVirtualizer } from "@tanstack/react-virtual"; +import { useEffect, useMemo, useRef } from "react"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuTrigger, +} from "@/ui/context-menu"; +import { + CopyIcon as Copy, + EyeIcon as Eye, + EyeSlashIcon as EyeSlash, + GitBranchIcon as GitBranch, + GitDiffIcon as GitDiff, + MagnifyingGlassIcon as Search, + XIcon, +} from "@/ui/icons"; +import { Button } from "@/ui/button"; +import { cn } from "@/utils/cn"; +import { + type GitLogFilterScope, + useGitLogPreferencesStore, +} from "../../stores/git-log-preferences.store"; +import type { GitCommit } from "../../types/git.types"; +import { layoutGitGraph } from "../../utils/git-graph-layout"; +import { matchesGitLogCommit } from "../../utils/git-log-filter"; +import { GitGraphRow } from "./git-graph-row"; + +const ROW_HEIGHT = 30; + +export function GitCommitTable({ + commits, + selectedCommit, + hasMore, + isLoadingMore, + onSelect, + onOpenDiff, + onCompareWithHead, + onCopyHash, + onCopyMessage, + onLoadMore, +}: { + commits: GitCommit[]; + selectedCommit: GitCommit | null; + hasMore: boolean; + isLoadingMore: boolean; + onSelect: (commit: GitCommit) => void; + onOpenDiff: (commit: GitCommit) => void; + onCompareWithHead: (commit: GitCommit) => void; + onCopyHash: (commit: GitCommit) => void; + onCopyMessage: (commit: GitCommit) => void; + onLoadMore: () => void; +}) { + const query = useGitLogPreferencesStore.use.filterQuery(); + const scope = useGitLogPreferencesStore.use.filterScope(); + const showDecorations = useGitLogPreferencesStore.use.showDecorations(); + const { setFilterQuery, setFilterScope, setShowDecorations } = + useGitLogPreferencesStore.use.actions(); + const scrollRef = useRef(null); + const layout = useMemo(() => layoutGitGraph(commits), [commits]); + const visibleRows = useMemo( + () => layout.rows.filter((row) => matchesGitLogCommit(row.commit, query, scope)), + [layout.rows, query, scope], + ); + const virtualizer = useVirtualizer({ + count: visibleRows.length, + getScrollElement: () => scrollRef.current, + estimateSize: () => ROW_HEIGHT, + overscan: 14, + }); + + useEffect(() => { + if (!selectedCommit) return; + const selectedIndex = visibleRows.findIndex((row) => row.commit.hash === selectedCommit.hash); + if (selectedIndex >= 0) virtualizer.scrollToIndex(selectedIndex, { align: "auto" }); + }, [selectedCommit, virtualizer, visibleRows]); + + const selectRowAt = (index: number) => { + const nextIndex = Math.max(0, Math.min(index, visibleRows.length - 1)); + const nextCommit = visibleRows[nextIndex]?.commit; + if (!nextCommit) return; + onSelect(nextCommit); + virtualizer.scrollToIndex(nextIndex, { align: "auto" }); + globalThis.requestAnimationFrame?.(() => { + scrollRef.current + ?.querySelector(`[data-git-commit-index="${nextIndex}"]`) + ?.focus(); + }); + }; + + const handleRowKeyDown = (event: React.KeyboardEvent, commit: GitCommit) => { + const currentIndex = visibleRows.findIndex((row) => row.commit.hash === commit.hash); + if (currentIndex < 0) return; + + switch (event.key) { + case "ArrowDown": + event.preventDefault(); + selectRowAt(currentIndex + 1); + break; + case "ArrowUp": + event.preventDefault(); + selectRowAt(currentIndex - 1); + break; + case "Home": + event.preventDefault(); + selectRowAt(0); + break; + case "End": + event.preventDefault(); + selectRowAt(visibleRows.length - 1); + break; + case "Enter": + event.preventDefault(); + onOpenDiff(commit); + break; + } + }; + + return ( +
+
+
+ + setFilterQuery(event.target.value)} + className="min-w-0 flex-1 bg-transparent outline-none placeholder:text-subtle-foreground" + placeholder={`${scope[0].toUpperCase()}${scope.slice(1)} filter`} + aria-label="Filter Git log" + /> + {query ? ( + + ) : null} +
+ + + + {visibleRows.length}/{commits.length} + +
+ +
+ Commit + Author + Date +
+ +
+ {visibleRows.length === 0 ? ( +
+ {query ? "No commits match this filter" : "No commits in this view"} +
+ ) : ( + <> +
+ {virtualizer.getVirtualItems().map((virtualRow) => { + const row = visibleRows[virtualRow.index]; + const isSelected = selectedCommit?.hash === row.commit.hash; + return ( + + onSelect(row.commit)} + onDoubleClick={() => onOpenDiff(row.commit)} + onContextMenu={() => onSelect(row.commit)} + onKeyDown={(event) => handleRowKeyDown(event, row.commit)} + title="Double-click or press Enter to open commit diff" + > + + + {row.commit.author} + + + {row.commit.date} + + + + onOpenDiff(row.commit)}> + + Open Commit Diff + Enter + + onCompareWithHead(row.commit)}> + + Compare with HEAD + + + onCopyHash(row.commit)}> + + Copy Commit Hash + + onCopyMessage(row.commit)}> + + Copy Commit Message + + + + ); + })} +
+ {hasMore ? ( +
+ +
+ ) : null} + + )} +
+
+ ); +} diff --git a/windows/tauri/src/features/git/components/log/git-graph-row.tsx b/windows/tauri/src/features/git/components/log/git-graph-row.tsx new file mode 100644 index 000000000..58543276c --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-graph-row.tsx @@ -0,0 +1,101 @@ +import { cn } from "@/utils/cn"; +import type { GitGraphLabel, GitGraphRow as GraphRow } from "../../utils/git-graph-layout"; + +const ROW_HEIGHT = 30; +const LANE_GAP = 13; +const GRAPH_PADDING = 8; +const GRAPH_COLORS = ["#55d68b", "#65a9ff", "#d77eea", "#f3aa59", "#e76c72", "#56c7cf"]; + +function graphColor(index: number) { + return GRAPH_COLORS[index % GRAPH_COLORS.length]; +} + +function labelClassName(label: GitGraphLabel) { + switch (label.kind) { + case "head": + return "border-sky-500/40 bg-sky-500/18 text-sky-300"; + case "remote": + return "border-indigo-500/40 bg-indigo-500/18 text-indigo-300"; + case "tag": + return "border-amber-500/40 bg-amber-500/18 text-amber-300"; + default: + return "border-emerald-500/40 bg-emerald-500/18 text-emerald-300"; + } +} + +export function GitGraphRow({ row, showDecorations }: { row: GraphRow; showDecorations: boolean }) { + const width = Math.max(30, row.laneCount * LANE_GAP + GRAPH_PADDING * 2); + const nodeX = GRAPH_PADDING + row.lane * LANE_GAP; + const middleY = ROW_HEIGHT / 2; + + return ( + <> + + +
+ {showDecorations && + row.labels.map((label, index) => ( + + {label.title} + + ))} + + {row.commit.message} + +
+ + ); +} diff --git a/windows/tauri/src/features/git/components/log/git-log-title-bar.tsx b/windows/tauri/src/features/git/components/log/git-log-title-bar.tsx new file mode 100644 index 000000000..c40690c2c --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-log-title-bar.tsx @@ -0,0 +1,107 @@ +import { + ArrowClockwiseIcon as Refresh, + CopyIcon as Copy, + GitBranchIcon, + GitDiffIcon as GitDiff, + MinusIcon, +} from "@/ui/icons"; +import { Button } from "@/ui/button"; + +export function GitLogTitleBar({ + referenceName, + isRefreshing, + isOpeningDiff, + isComparing, + hasSelectedCommit, + canCompareWithHead, + onShowAll, + onRefresh, + onOpenDiff, + onCompareWithHead, + onCopyHash, + onClose, +}: { + referenceName: string; + isRefreshing: boolean; + isOpeningDiff: boolean; + isComparing: boolean; + hasSelectedCommit: boolean; + canCompareWithHead: boolean; + onShowAll: () => void; + onRefresh: () => void; + onOpenDiff: () => void; + onCompareWithHead: () => void; + onCopyHash: () => void; + onClose: () => void; +}) { + return ( +
+
+ + Git + + + Read-only + +
+
+ + + +
+
+ ); +} diff --git a/windows/tauri/src/features/git/components/log/git-log-tool-window.tsx b/windows/tauri/src/features/git/components/log/git-log-tool-window.tsx new file mode 100644 index 000000000..87fcbc8f9 --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-log-tool-window.tsx @@ -0,0 +1,193 @@ +import { useEffect, useMemo, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/ui/button"; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui/resizable"; +import { tryWriteClipboardText } from "@/utils/clipboard"; +import { useProjectStore } from "@/features/window/stores/project.store"; +import { useUIState } from "@/features/window/stores/ui-state.store"; +import { useGitLogController } from "../../hooks/use-git-log-controller"; +import { useGitDiffActions } from "../../hooks/use-git-diff-actions"; +import { useGitLogPreferencesStore } from "../../stores/git-log-preferences.store"; +import { useRepositoryStore } from "../../stores/git-repository.store"; +import type { GitCommit, GitFile } from "../../types/git.types"; +import type { + WorkingTreeDiffEntry, + WorkingTreeDiffScope, +} from "../../services/working-tree-diff-loader"; +import { GitCommitInspector } from "./git-commit-inspector"; +import { GitCommitTable } from "./git-commit-table"; +import { GitLogTitleBar } from "./git-log-title-bar"; +import { GitReferenceTree } from "./git-reference-tree"; + +export function GitLogToolWindow() { + const activeRepoPath = useRepositoryStore.use.activeRepoPath(); + const rootFolderPath = useProjectStore((state) => state.rootFolderPath); + const repoPath = activeRepoPath ?? rootFolderPath ?? null; + const setIsBottomPaneVisible = useUIState((state) => state.setIsBottomPaneVisible); + const { + history, + loadState, + error, + selectedReference, + isLoadingMore, + selectReference, + refresh, + loadMore, + } = useGitLogController(repoPath); + const [selectedCommit, setSelectedCommit] = useState(null); + const mainPanelLayout = useGitLogPreferencesStore.use.mainPanelLayout(); + const { setMainPanelLayout } = useGitLogPreferencesStore.use.actions(); + const currentReference = useMemo( + () => history.references.find((reference) => reference.isCurrent) ?? null, + [history.references], + ); + const commitByHash = useMemo( + () => new Map(history.commits.map((commit) => [commit.hash, commit] as const)), + [history.commits], + ); + const emptyWorkingTreeEntries = useMemo>( + () => ({ + all: [], + staged: [], + unstaged: [], + }), + [], + ); + const emptyGitFileByPath = useMemo(() => new Map(), []); + const { isLoadingCommitDiff, isLoadingBranchDiff, viewCommitDiff, viewBranchDiff } = + useGitDiffActions({ + activeRepoPath: repoPath, + gitFileByPath: emptyGitFileByPath, + workingTreeDiffEntriesByScope: emptyWorkingTreeEntries, + commitByHash, + currentBranch: currentReference?.shortName, + }); + + useEffect(() => { + setSelectedCommit((current) => { + if (current && commitByHash.has(current.hash)) return commitByHash.get(current.hash) ?? null; + return history.commits[0] ?? null; + }); + }, [commitByHash, history.commits]); + + const openDiff = (commit: GitCommit, filePath?: string) => { + if (isLoadingCommitDiff) return; + void viewCommitDiff(commit.hash, filePath); + }; + + const copyCommitText = async (text: string, label: string) => { + if (await tryWriteClipboardText(text)) { + toast.success(`${label} copied`); + return; + } + toast.error(`Unable to copy ${label.toLocaleLowerCase()}`); + }; + + const comparisonBaseRef = + selectedReference && !selectedReference.isCurrent + ? selectedReference.fullName + : selectedCommit?.hash; + + return ( +
+ { + setSelectedCommit(null); + selectReference(null); + }} + onRefresh={() => void refresh()} + onOpenDiff={() => { + if (selectedCommit) openDiff(selectedCommit); + }} + onCompareWithHead={() => { + if (comparisonBaseRef) void viewBranchDiff(comparisonBaseRef); + }} + onCopyHash={() => { + if (selectedCommit) void copyCommitText(selectedCommit.hash, "Commit hash"); + }} + onClose={() => setIsBottomPaneVisible(false)} + /> + + {loadState === "failed" && history.commits.length > 0 ? ( +
+ {error ?? "Unable to refresh Git log."} + +
+ ) : null} + + {!repoPath ? ( +
+
No repository open
+
Open a Git workspace to view its log.
+
+ ) : loadState === "loading" && history.commits.length === 0 ? ( +
+ Loading Git log… +
+ ) : loadState === "failed" && history.commits.length === 0 ? ( +
+
{error ?? "Unable to load Git log."}
+ +
+ ) : ( + { + if (meta.isUserInteraction) setMainPanelLayout(layout); + }} + > + + { + setSelectedCommit(null); + selectReference(reference); + }} + /> + + + + openDiff(commit)} + onCompareWithHead={(commit) => void viewBranchDiff(commit.hash)} + onCopyHash={(commit) => void copyCommitText(commit.hash, "Commit hash")} + onCopyMessage={(commit) => + void copyCommitText( + [commit.message, commit.description].filter(Boolean).join("\n\n"), + "Commit message", + ) + } + onLoadMore={() => void loadMore()} + /> + + + + + + + )} +
+ ); +} diff --git a/windows/tauri/src/features/git/components/log/git-reference-tree.tsx b/windows/tauri/src/features/git/components/log/git-reference-tree.tsx new file mode 100644 index 000000000..0f1117538 --- /dev/null +++ b/windows/tauri/src/features/git/components/log/git-reference-tree.tsx @@ -0,0 +1,190 @@ +import { + CaretDownIcon, + CaretRightIcon, + FolderIcon, + GitBranchIcon, + NetworkIcon, + TagIcon, +} from "@/ui/icons"; +import { useMemo } from "react"; +import { cn } from "@/utils/cn"; +import { useGitLogPreferencesStore } from "../../stores/git-log-preferences.store"; +import type { GitReference, GitReferenceKind } from "../../types/git.types"; +import { buildGitReferenceTree, type GitReferenceTreeNode } from "../../utils/git-reference-tree"; + +const SECTIONS: Array<{ kind: GitReferenceKind; title: string }> = [ + { kind: "local", title: "Local" }, + { kind: "remote", title: "Remote" }, + { kind: "tag", title: "Tags" }, +]; + +function ReferenceIcon({ kind }: { kind: GitReferenceKind }) { + if (kind === "tag") return ; + if (kind === "remote") return ; + return ; +} + +function ReferenceNode({ + node, + kind, + depth, + selectedFullName, + collapsedGroups, + onToggleGroup, + onSelect, +}: { + node: GitReferenceTreeNode; + kind: GitReferenceKind; + depth: number; + selectedFullName?: string; + collapsedGroups: Set; + onToggleGroup: (id: string) => void; + onSelect: (reference: GitReference) => void; +}) { + const isGroup = node.children.length > 0; + const isCollapsed = collapsedGroups.has(node.id); + const left = 10 + depth * 14; + + return ( + <> +
+ {isGroup ? ( + + ) : ( + + )} + +
+ {!isCollapsed && + node.children.map((child) => ( + + ))} + + ); +} + +export function GitReferenceTree({ + references, + selectedReference, + onSelect, +}: { + references: GitReference[]; + selectedReference: GitReference | null; + onSelect: (reference: GitReference | null) => void; +}) { + const collapsedSectionIds = useGitLogPreferencesStore.use.collapsedReferenceSections(); + const collapsedGroupIds = useGitLogPreferencesStore.use.collapsedReferenceGroups(); + const { toggleReferenceSection, toggleReferenceGroup } = useGitLogPreferencesStore.use.actions(); + const collapsedSections = useMemo(() => new Set(collapsedSectionIds), [collapsedSectionIds]); + const collapsedGroups = useMemo(() => new Set(collapsedGroupIds), [collapsedGroupIds]); + const currentReference = references.find((reference) => reference.isCurrent) ?? null; + const trees = useMemo( + () => new Map(SECTIONS.map(({ kind }) => [kind, buildGitReferenceTree(references, kind)])), + [references], + ); + + return ( +
+
+ References + {references.length} +
+
+ + + {SECTIONS.map(({ kind, title }) => { + const collapsed = collapsedSections.has(kind); + const nodes = trees.get(kind) ?? []; + return ( +
+ + {!collapsed && + (nodes.length ? ( + nodes.map((node) => ( + + )) + ) : ( +
None
+ ))} +
+ ); + })} +
+
+ ); +} diff --git a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts index 1966e7d2a..ff28ebca0 100644 --- a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts +++ b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts @@ -24,6 +24,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl useRepositoryStore.use.actions(); const gitActions = useGitStore((state) => state.actions); const gitStatus = useGitStore((state) => state.gitStatus); + const loadedCommitCount = useGitStore((state) => state.commits.length); const autoRefreshGitStatus = useSettingsStore((state) => state.settings.autoRefreshGitStatus); const requestIdRef = useRef(0); const refreshPromisesRef = useRef(new Map>()); @@ -95,7 +96,9 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl getGitStatus(repoPath), shouldRefreshRefs ? getBranches(repoPath) : Promise.resolve(undefined), shouldRefreshStashes ? getStashes(repoPath) : Promise.resolve(undefined), - shouldRefreshHistory ? getGitHistory(repoPath, 50) : Promise.resolve(undefined), + shouldRefreshHistory + ? getGitHistory(repoPath, Math.max(loadedCommitCount, 50)) + : Promise.resolve(undefined), ]); if ( @@ -134,7 +137,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl refreshPromisesRef.current.set(refreshKey, request); return request; }, - [activeRepoPath, gitActions], + [activeRepoPath, gitActions, loadedCommitCount], ); const refresh = useCallback(async () => { diff --git a/windows/tauri/src/features/git/hooks/use-git-log-controller.ts b/windows/tauri/src/features/git/hooks/use-git-log-controller.ts new file mode 100644 index 000000000..3e96c7907 --- /dev/null +++ b/windows/tauri/src/features/git/hooks/use-git-log-controller.ts @@ -0,0 +1,115 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { getGitHistory } from "../api/git-commits-api"; +import type { GitHistorySnapshot, GitReference } from "../types/git.types"; + +type GitLogLoadState = "idle" | "loading" | "ready" | "failed"; + +const COMMITS_PER_PAGE = 50; +const MAX_COMMITS = 5_000; +const EMPTY_HISTORY: GitHistorySnapshot = { references: [], commits: [], hasMore: false }; + +export function useGitLogController(repoPath: string | null) { + const [history, setHistory] = useState(EMPTY_HISTORY); + const [loadState, setLoadState] = useState("idle"); + const [error, setError] = useState(null); + const [selectedReference, setSelectedReferenceState] = useState(null); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const requestIdRef = useRef(0); + const historyRef = useRef(history); + const selectedReferenceRef = useRef(selectedReference); + + historyRef.current = history; + selectedReferenceRef.current = selectedReference; + + const load = useCallback( + async ({ + reference, + limit, + loadingMore = false, + }: { + reference: GitReference | null; + limit: number; + loadingMore?: boolean; + }) => { + if (!repoPath) return; + + const requestId = ++requestIdRef.current; + setError(null); + if (loadingMore) setIsLoadingMore(true); + else setLoadState("loading"); + + try { + const snapshot = await getGitHistory(repoPath, limit, reference?.fullName); + if (requestId !== requestIdRef.current) return; + if (!snapshot) { + setLoadState("failed"); + setError("Unable to load Git history for this repository."); + return; + } + + setHistory({ + ...snapshot, + hasMore: snapshot.hasMore && limit < MAX_COMMITS, + }); + setLoadState("ready"); + } catch (loadError) { + if (requestId !== requestIdRef.current) return; + setLoadState("failed"); + setError(loadError instanceof Error ? loadError.message : "Unable to load Git history."); + } finally { + if (requestId === requestIdRef.current) setIsLoadingMore(false); + } + }, + [repoPath], + ); + + useEffect(() => { + requestIdRef.current += 1; + setHistory(EMPTY_HISTORY); + setSelectedReferenceState(null); + setIsLoadingMore(false); + setError(null); + + if (!repoPath) { + setLoadState("idle"); + return; + } + void load({ reference: null, limit: COMMITS_PER_PAGE }); + + return () => { + requestIdRef.current += 1; + }; + }, [load, repoPath]); + + const selectReference = useCallback( + (reference: GitReference | null) => { + selectedReferenceRef.current = reference; + setSelectedReferenceState(reference); + void load({ reference, limit: COMMITS_PER_PAGE }); + }, + [load], + ); + + const refresh = useCallback(() => { + const limit = Math.max(COMMITS_PER_PAGE, historyRef.current.commits.length); + return load({ reference: selectedReferenceRef.current, limit }); + }, [load]); + + const loadMore = useCallback(() => { + const currentHistory = historyRef.current; + if (!currentHistory.hasMore || isLoadingMore) return Promise.resolve(); + const limit = Math.min(currentHistory.commits.length + COMMITS_PER_PAGE, MAX_COMMITS); + return load({ reference: selectedReferenceRef.current, limit, loadingMore: true }); + }, [isLoadingMore, load]); + + return { + history, + loadState, + error, + selectedReference, + isLoadingMore, + selectReference, + refresh, + loadMore, + }; +} diff --git a/windows/tauri/src/features/git/stores/git-log-preferences.store.test.ts b/windows/tauri/src/features/git/stores/git-log-preferences.store.test.ts new file mode 100644 index 000000000..8ddc09bf1 --- /dev/null +++ b/windows/tauri/src/features/git/stores/git-log-preferences.store.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, test } from "bun:test"; +import { useGitLogPreferencesStore } from "./git-log-preferences.store"; + +describe("Git Log preferences", () => { + test("persists read-only view preferences through focused actions", () => { + const actions = useGitLogPreferencesStore.getState().actions; + + actions.setFilterQuery("graph"); + actions.setFilterScope("author"); + actions.setShowDecorations(false); + actions.setMainPanelLayout({ references: 20, commits: 55, inspector: 25 }); + actions.setInspectorPanelLayout({ files: 70, details: 30 }); + actions.toggleReferenceSection("remote"); + actions.toggleReferenceGroup("remote:origin"); + + expect(useGitLogPreferencesStore.getState()).toMatchObject({ + filterQuery: "graph", + filterScope: "author", + showDecorations: false, + mainPanelLayout: { references: 20, commits: 55, inspector: 25 }, + inspectorPanelLayout: { files: 70, details: 30 }, + collapsedReferenceSections: ["remote"], + collapsedReferenceGroups: ["remote:origin"], + }); + + actions.setFilterQuery(""); + actions.setFilterScope("text"); + actions.setShowDecorations(true); + actions.setMainPanelLayout({ references: 19, commits: 57, inspector: 24 }); + actions.setInspectorPanelLayout({ files: 62, details: 38 }); + actions.toggleReferenceSection("remote"); + actions.toggleReferenceGroup("remote:origin"); + }); +}); diff --git a/windows/tauri/src/features/git/stores/git-log-preferences.store.ts b/windows/tauri/src/features/git/stores/git-log-preferences.store.ts new file mode 100644 index 000000000..44e09b21a --- /dev/null +++ b/windows/tauri/src/features/git/stores/git-log-preferences.store.ts @@ -0,0 +1,86 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { createSelectors } from "@/utils/zustand-selectors"; +import { createSafeJSONStorage } from "@/utils/zustand-storage"; +import type { GitReferenceKind } from "../types/git.types"; + +export type GitLogFilterScope = "text" | "author" | "branch"; + +export interface GitLogPanelLayout { + [panelId: string]: number; +} + +interface GitLogPreferencesStore { + filterQuery: string; + filterScope: GitLogFilterScope; + showDecorations: boolean; + mainPanelLayout: GitLogPanelLayout; + inspectorPanelLayout: GitLogPanelLayout; + collapsedReferenceSections: GitReferenceKind[]; + collapsedReferenceGroups: string[]; + actions: { + setFilterQuery: (query: string) => void; + setFilterScope: (scope: GitLogFilterScope) => void; + setShowDecorations: (show: boolean) => void; + setMainPanelLayout: (layout: GitLogPanelLayout) => void; + setInspectorPanelLayout: (layout: GitLogPanelLayout) => void; + toggleReferenceSection: (kind: GitReferenceKind) => void; + toggleReferenceGroup: (id: string) => void; + }; +} + +const DEFAULT_MAIN_LAYOUT: GitLogPanelLayout = { + references: 19, + commits: 57, + inspector: 24, +}; + +const DEFAULT_INSPECTOR_LAYOUT: GitLogPanelLayout = { + files: 62, + details: 38, +}; + +function toggleListItem(items: T[], item: T): T[] { + return items.includes(item) ? items.filter((value) => value !== item) : [...items, item]; +} + +const useGitLogPreferencesStoreBase = create()( + persist( + (set) => ({ + filterQuery: "", + filterScope: "text", + showDecorations: true, + mainPanelLayout: DEFAULT_MAIN_LAYOUT, + inspectorPanelLayout: DEFAULT_INSPECTOR_LAYOUT, + collapsedReferenceSections: [], + collapsedReferenceGroups: [], + actions: { + setFilterQuery: (filterQuery) => set({ filterQuery }), + setFilterScope: (filterScope) => set({ filterScope }), + setShowDecorations: (showDecorations) => set({ showDecorations }), + setMainPanelLayout: (mainPanelLayout) => set({ mainPanelLayout }), + setInspectorPanelLayout: (inspectorPanelLayout) => set({ inspectorPanelLayout }), + toggleReferenceSection: (kind) => + set((state) => ({ + collapsedReferenceSections: toggleListItem(state.collapsedReferenceSections, kind), + })), + toggleReferenceGroup: (id) => + set((state) => ({ + collapsedReferenceGroups: toggleListItem(state.collapsedReferenceGroups, id), + })), + }, + }), + { + name: "git-log-preferences", + storage: createSafeJSONStorage>(), + partialize: ({ actions: _, ...preferences }) => preferences, + merge: (persistedState, currentState) => ({ + ...currentState, + ...(persistedState as Partial), + actions: currentState.actions, + }), + }, + ), +); + +export const useGitLogPreferencesStore = createSelectors(useGitLogPreferencesStoreBase); diff --git a/windows/tauri/src/features/git/stores/git.store.test.ts b/windows/tauri/src/features/git/stores/git.store.test.ts index 70d18e330..be3624771 100644 --- a/windows/tauri/src/features/git/stores/git.store.test.ts +++ b/windows/tauri/src/features/git/stores/git.store.test.ts @@ -11,9 +11,12 @@ const { createGitStore } = await import("./git.store"); const commit = (index: number): GitCommit => ({ hash: `commit-${index}`, + shortHash: `commit-${index}`, + parentHashes: [], message: `Commit ${index}`, author: "Developer", date: "2026/08/16 10:00", + decorations: "", }); const commits = (count: number): GitCommit[] => @@ -43,7 +46,7 @@ describe("Git history pagination", () => { test("requests a larger cumulative snapshot instead of an ignored offset", async () => { const store = createGitStore(); loadInitialHistory(store, "C:/repo", commits(50)); - getGitHistory.mockResolvedValue({ commits: commits(100), hasMore: true }); + getGitHistory.mockResolvedValue({ references: [], commits: commits(100), hasMore: true }); await store.getState().actions.loadMoreCommits("C:/repo"); @@ -55,7 +58,7 @@ describe("Git history pagination", () => { test("uses the shared core hasMore flag at the end of history", async () => { const store = createGitStore(); loadInitialHistory(store, "C:/repo", commits(50)); - getGitHistory.mockResolvedValue({ commits: commits(73), hasMore: false }); + getGitHistory.mockResolvedValue({ references: [], commits: commits(73), hasMore: false }); await store.getState().actions.loadMoreCommits("C:/repo"); @@ -89,7 +92,7 @@ describe("Git history pagination", () => { const pending = store.getState().actions.loadMoreCommits("C:/repo-a"); store.getState().actions.prepareRepositoryLoad("C:/repo-b"); - resolveHistory({ commits: commits(100), hasMore: true }); + resolveHistory({ references: [], commits: commits(100), hasMore: true }); await pending; expect(store.getState().currentRepoPath).toBe("C:/repo-b"); diff --git a/windows/tauri/src/features/git/types/git.types.ts b/windows/tauri/src/features/git/types/git.types.ts index d9646703b..86b26718d 100644 --- a/windows/tauri/src/features/git/types/git.types.ts +++ b/windows/tauri/src/features/git/types/git.types.ts @@ -13,18 +13,37 @@ export interface GitStatus { export interface GitCommit { hash: string; + shortHash: string; + parentHashes: string[]; message: string; description?: string; author: string; email?: string; date: string; + decorations: string; +} + +export type GitReferenceKind = "local" | "remote" | "tag"; + +export interface GitReference { + fullName: string; + shortName: string; + kind: GitReferenceKind; + isCurrent: boolean; + upstreamShortName?: string; } export interface GitHistorySnapshot { + references: GitReference[]; commits: GitCommit[]; hasMore: boolean; } +export interface GitCommitFile { + status: string; + path: string; +} + export interface GitDiffLine { line_type: "added" | "removed" | "context" | "header"; content: string; diff --git a/windows/tauri/src/features/git/utils/git-graph-layout.test.ts b/windows/tauri/src/features/git/utils/git-graph-layout.test.ts new file mode 100644 index 000000000..fd6825147 --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-graph-layout.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, test } from "bun:test"; +import type { GitCommit } from "../types/git.types"; +import { layoutGitGraph, parseGitDecorations } from "./git-graph-layout"; + +const commit = ( + hash: string, + parentHashes: string[] = [], + decorations = "", +): GitCommit => ({ + hash, + shortHash: hash, + parentHashes, + message: hash, + author: "Developer", + date: "2026/08/16 10:00", + decorations, +}); + +describe("Git graph layout", () => { + test("keeps a linear history in one fixed lane", () => { + const layout = layoutGitGraph([commit("three", ["two"]), commit("two", ["one"]), commit("one")]); + + expect(layout.laneCount).toBe(1); + expect(layout.rows.map((row) => row.lane)).toEqual([0, 0, 0]); + expect(layout.hasMissingParents).toBe(false); + }); + + test("opens a stable secondary lane for a merge parent", () => { + const layout = layoutGitGraph([ + commit("merge", ["feature", "root"], "HEAD -> main"), + commit("feature", ["root"], "feature/orders"), + commit("root"), + ]); + + expect(layout.rows[0].parentEdges).toHaveLength(2); + expect(layout.rows[0].parentEdges.map((edge) => edge.targetLane)).toEqual([0, 1]); + expect(layout.rows[1].parentEdges[0].targetLane).toBe(1); + expect(layout.rows[2].lane).toBe(1); + expect(layout.hasMissingParents).toBe(false); + }); + + test("marks parents outside the cumulative snapshot as missing", () => { + const layout = layoutGitGraph([commit("visible", ["not-loaded"])]); + + expect(layout.hasMissingParents).toBe(true); + expect(layout.rows[0].parentEdges[0]).toMatchObject({ targetLane: null, isMissing: true }); + }); + + test("parses head, branch, remote, and tag decorations", () => { + expect(parseGitDecorations("HEAD -> main, origin/main, tag: v1.0.0")).toEqual([ + { title: "HEAD", kind: "head" }, + { title: "main", kind: "branch" }, + { title: "origin/main", kind: "remote" }, + { title: "v1.0.0", kind: "tag" }, + ]); + }); +}); diff --git a/windows/tauri/src/features/git/utils/git-graph-layout.ts b/windows/tauri/src/features/git/utils/git-graph-layout.ts new file mode 100644 index 000000000..8c3d458c9 --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-graph-layout.ts @@ -0,0 +1,150 @@ +import type { GitCommit } from "../types/git.types"; + +export type GitGraphLabelKind = "head" | "branch" | "remote" | "tag"; + +export interface GitGraphLabel { + title: string; + kind: GitGraphLabelKind; +} + +export interface GitGraphEdge { + id: string; + parentHash: string; + targetLane: number | null; + colorIndex: number; + isMissing: boolean; +} + +export interface GitGraphRow { + commit: GitCommit; + lane: number; + laneCount: number; + incomingLaneColors: Array; + parentEdges: GitGraphEdge[]; + labels: GitGraphLabel[]; +} + +export interface GitGraphLayout { + rows: GitGraphRow[]; + laneCount: number; + hasMissingParents: boolean; +} + +interface Lane { + hash: string; + colorIndex: number; +} + +function claimSlot(slots: Array): number { + const freeSlot = slots.findIndex((slot) => slot === null); + if (freeSlot >= 0) return freeSlot; + slots.push(null); + return slots.length - 1; +} + +export function parseGitDecorations(decorations: string): GitGraphLabel[] { + return decorations.split(",").flatMap((value): GitGraphLabel[] => { + const raw = value.trim(); + if (!raw) return []; + if (raw === "HEAD") return [{ title: "HEAD", kind: "head" }]; + if (raw.startsWith("HEAD -> ")) { + return [ + { title: "HEAD", kind: "head" }, + { title: raw.slice("HEAD -> ".length), kind: "branch" }, + ]; + } + if (raw.startsWith("tag: ")) return [{ title: raw.slice("tag: ".length), kind: "tag" }]; + if (raw.startsWith("refs/tags/")) { + return [{ title: raw.slice("refs/tags/".length), kind: "tag" }]; + } + if (raw.startsWith("origin/") || raw.startsWith("refs/remotes/")) { + return [ + { + title: raw.startsWith("refs/remotes/") ? raw.slice("refs/remotes/".length) : raw, + kind: "remote", + }, + ]; + } + return [{ title: raw, kind: "branch" }]; + }); +} + +export function layoutGitGraph(commits: GitCommit[]): GitGraphLayout { + if (commits.length === 0) return { rows: [], laneCount: 0, hasMissingParents: false }; + + const knownHashes = new Set(commits.map((commit) => commit.hash)); + const slots: Array = []; + const rows: GitGraphRow[] = []; + let nextColorIndex = 0; + let maximumLaneCount = 0; + let hasMissingParents = false; + + for (const commit of commits) { + let currentLane = slots.findIndex((slot) => slot?.hash === commit.hash); + if (currentLane < 0) { + currentLane = claimSlot(slots); + slots[currentLane] = { hash: commit.hash, colorIndex: nextColorIndex++ }; + } + + const incomingLaneColors = slots.map((slot) => slot?.colorIndex ?? null); + const currentColorIndex = slots[currentLane]?.colorIndex ?? 0; + slots[currentLane] = null; + + const parentEdges: GitGraphEdge[] = []; + commit.parentHashes.forEach((parentHash, parentIndex) => { + if (!knownHashes.has(parentHash)) { + hasMissingParents = true; + parentEdges.push({ + id: `${commit.hash}:${parentIndex}:${parentHash}`, + parentHash, + targetLane: null, + colorIndex: parentIndex === 0 ? currentColorIndex : nextColorIndex, + isMissing: true, + }); + return; + } + + let targetLane = slots.findIndex((slot) => slot?.hash === parentHash); + let colorIndex: number; + if (targetLane >= 0) { + colorIndex = slots[targetLane]?.colorIndex ?? currentColorIndex; + } else if (parentIndex === 0) { + targetLane = currentLane; + colorIndex = currentColorIndex; + slots[targetLane] = { hash: parentHash, colorIndex }; + } else { + targetLane = claimSlot(slots); + colorIndex = nextColorIndex++; + slots[targetLane] = { hash: parentHash, colorIndex }; + } + + parentEdges.push({ + id: `${commit.hash}:${parentIndex}:${parentHash}`, + parentHash, + targetLane, + colorIndex, + isMissing: false, + }); + }); + + while (slots.length > 0 && slots[slots.length - 1] === null) slots.pop(); + const edgeLaneCount = Math.max(-1, ...parentEdges.map((edge) => edge.targetLane ?? -1)) + 1; + const laneCount = Math.max( + incomingLaneColors.length, + slots.length, + currentLane + 1, + edgeLaneCount, + ); + maximumLaneCount = Math.max(maximumLaneCount, laneCount); + rows.push({ + commit, + lane: currentLane, + laneCount, + incomingLaneColors, + parentEdges, + labels: parseGitDecorations(commit.decorations), + }); + } + + return { rows, laneCount: Math.max(1, maximumLaneCount), hasMissingParents }; +} diff --git a/windows/tauri/src/features/git/utils/git-log-filter.test.ts b/windows/tauri/src/features/git/utils/git-log-filter.test.ts new file mode 100644 index 000000000..0d0e9da9c --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-log-filter.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, test } from "bun:test"; +import type { GitCommit } from "../types/git.types"; +import { matchesGitLogCommit } from "./git-log-filter"; + +const COMMIT: GitCommit = { + hash: "0123456789abcdef", + shortHash: "0123456", + parentHashes: ["fedcba9876543210"], + message: "Add Git graph", + description: "Render stable lanes", + author: "Lithe Developer", + email: "dev@example.com", + date: "2026-08-16T10:00:00Z", + decorations: "HEAD -> preview/0.3.0, origin/preview/0.3.0, tag: v0.3.0", +}; + +describe("Git Log filters", () => { + test("matches text against the subject, body, and hashes without case sensitivity", () => { + expect(matchesGitLogCommit(COMMIT, "git GRAPH", "text")).toBe(true); + expect(matchesGitLogCommit(COMMIT, "stable lanes", "text")).toBe(true); + expect(matchesGitLogCommit(COMMIT, "0123456", "text")).toBe(true); + expect(matchesGitLogCommit(COMMIT, "someone else", "text")).toBe(false); + }); + + test("keeps author and branch searches scoped to their displayed metadata", () => { + expect(matchesGitLogCommit(COMMIT, "dev@example.com", "author")).toBe(true); + expect(matchesGitLogCommit(COMMIT, "origin/preview", "branch")).toBe(true); + expect(matchesGitLogCommit(COMMIT, "stable lanes", "author")).toBe(false); + }); +}); diff --git a/windows/tauri/src/features/git/utils/git-log-filter.ts b/windows/tauri/src/features/git/utils/git-log-filter.ts new file mode 100644 index 000000000..c3d18c5ed --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-log-filter.ts @@ -0,0 +1,19 @@ +import type { GitLogFilterScope } from "../stores/git-log-preferences.store"; +import type { GitCommit } from "../types/git.types"; + +export function matchesGitLogCommit( + commit: GitCommit, + query: string, + scope: GitLogFilterScope, +): boolean { + const normalizedQuery = query.trim().toLocaleLowerCase(); + if (!normalizedQuery) return true; + + const fields = + scope === "author" + ? [commit.author, commit.email ?? ""] + : scope === "branch" + ? [commit.decorations] + : [commit.message, commit.description ?? "", commit.hash, commit.shortHash]; + return fields.some((field) => field.toLocaleLowerCase().includes(normalizedQuery)); +} diff --git a/windows/tauri/src/features/git/utils/git-reference-tree.test.ts b/windows/tauri/src/features/git/utils/git-reference-tree.test.ts new file mode 100644 index 000000000..674a79bb6 --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-reference-tree.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, test } from "bun:test"; +import type { GitReference } from "../types/git.types"; +import { buildGitReferenceTree } from "./git-reference-tree"; + +const reference = (shortName: string): GitReference => ({ + fullName: `refs/remotes/${shortName}`, + shortName, + kind: "remote", + isCurrent: false, +}); + +describe("Git reference tree", () => { + test("groups slash-delimited references without losing leaf references", () => { + const tree = buildGitReferenceTree( + [reference("origin/main"), reference("origin/feature/orders")], + "remote", + ); + + expect(tree).toHaveLength(1); + expect(tree[0].name).toBe("origin"); + expect(tree[0].children.map((node) => node.name)).toEqual(["feature", "main"]); + expect(tree[0].children[0].children[0].reference?.shortName).toBe("origin/feature/orders"); + }); +}); diff --git a/windows/tauri/src/features/git/utils/git-reference-tree.ts b/windows/tauri/src/features/git/utils/git-reference-tree.ts new file mode 100644 index 000000000..6cc18d9cc --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-reference-tree.ts @@ -0,0 +1,49 @@ +import type { GitReference, GitReferenceKind } from "../types/git.types"; + +export interface GitReferenceTreeNode { + id: string; + name: string; + path: string; + reference?: GitReference; + children: GitReferenceTreeNode[]; +} + +interface MutableReferenceNode extends GitReferenceTreeNode { + children: MutableReferenceNode[]; +} + +export function buildGitReferenceTree( + references: GitReference[], + kind: GitReferenceKind, +): GitReferenceTreeNode[] { + const roots: MutableReferenceNode[] = []; + + for (const reference of references.filter((item) => item.kind === kind)) { + const parts = reference.shortName.split("/").filter(Boolean); + let siblings = roots; + let path = ""; + + parts.forEach((part, index) => { + path = path ? `${path}/${part}` : part; + let node = siblings.find((candidate) => candidate.name === part); + if (!node) { + node = { id: `${kind}:${path}`, name: part, path, children: [] }; + siblings.push(node); + } + if (index === parts.length - 1) node.reference = reference; + siblings = node.children; + }); + } + + const sortNodes = (nodes: MutableReferenceNode[]) => { + nodes.sort((left, right) => { + if (Boolean(left.children.length) !== Boolean(right.children.length)) { + return left.children.length ? -1 : 1; + } + return left.name.localeCompare(right.name); + }); + nodes.forEach((node) => sortNodes(node.children)); + }; + sortNodes(roots); + return roots; +} diff --git a/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx b/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx index e870517fe..8c0f9c317 100644 --- a/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx +++ b/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx @@ -4,6 +4,7 @@ import { isBackendCapabilityAvailable } from "@/config/backend-capabilities"; import DebuggerView from "@/features/debugger/components/debugger-view"; import RunPane from "@/features/run/components/run-pane"; import { useBufferStore } from "@/features/editor/stores/buffer.store"; +import { GitLogToolWindow } from "@/features/git/components/log/git-log-tool-window"; import { BOTTOM_PANE_ID } from "@/features/panes/constants/pane"; import { usePaneStore } from "@/features/panes/stores/pane.store"; import { activateBufferInPaneAndSync } from "@/features/panes/utils/pane-activation"; @@ -280,6 +281,12 @@ const BottomPane = () => { {bottomPaneBufferIds.length > 0 ? : null}
)} + + {bottomPaneActiveTab === "gitLog" && ( +
+ +
+ )} ); diff --git a/windows/tauri/src/features/window/stores/ui-state/types/ui-state.types.ts b/windows/tauri/src/features/window/stores/ui-state/types/ui-state.types.ts index c539c7476..c3613f502 100644 --- a/windows/tauri/src/features/window/stores/ui-state/types/ui-state.types.ts +++ b/windows/tauri/src/features/window/stores/ui-state/types/ui-state.types.ts @@ -19,7 +19,8 @@ export type BottomPaneTab = | "diagnostics" | "references" | "buffers" - | "run"; + | "run" + | "gitLog"; export interface QuickEditSelection { text: string; diff --git a/windows/tauri/src/platform/core-result-adapter.history.test.ts b/windows/tauri/src/platform/core-result-adapter.history.test.ts index 98f2735f5..7c6b46e3f 100644 --- a/windows/tauri/src/platform/core-result-adapter.history.test.ts +++ b/windows/tauri/src/platform/core-result-adapter.history.test.ts @@ -8,13 +8,25 @@ describe("git history result adaptation", () => { "git_log", { repoPath: "C:/work", limit: 50 }, { + references: [ + { + fullName: "refs/heads/main", + shortName: "main", + kind: "local", + isCurrent: true, + upstreamShortName: "origin/main", + }, + ], commits: [ { hash: "abc123", + shortHash: "abc123", + parentHashes: ["parent123"], subject: "First commit", authorName: "Developer", authorEmail: "developer@example.invalid", date: "2026/08/16 10:00", + decorations: "HEAD -> main, origin/main", }, ], hasMore: true, @@ -22,13 +34,25 @@ describe("git history result adaptation", () => { ); expect(result).toEqual({ + references: [ + { + fullName: "refs/heads/main", + shortName: "main", + kind: "local", + isCurrent: true, + upstreamShortName: "origin/main", + }, + ], commits: [ { hash: "abc123", + shortHash: "abc123", + parentHashes: ["parent123"], message: "First commit", author: "Developer", email: "developer@example.invalid", date: "2026/08/16 10:00", + decorations: "HEAD -> main, origin/main", }, ], hasMore: true, @@ -37,6 +61,7 @@ describe("git history result adaptation", () => { test("defaults missing history fields to an exhausted empty snapshot", () => { expect(adaptCoreResult("git_log", undefined, {})).toEqual({ + references: [], commits: [], hasMore: false, }); diff --git a/windows/tauri/src/platform/core-result-adapter.ts b/windows/tauri/src/platform/core-result-adapter.ts index 016ff75fb..1b018f0a4 100644 --- a/windows/tauri/src/platform/core-result-adapter.ts +++ b/windows/tauri/src/platform/core-result-adapter.ts @@ -120,13 +120,25 @@ export function adaptCoreResult( } as T; case "git_log": return { + references: Array.isArray(data.references) + ? data.references.map((reference: JsonRecord) => ({ + fullName: reference.fullName, + shortName: reference.shortName, + kind: reference.kind, + isCurrent: Boolean(reference.isCurrent), + upstreamShortName: reference.upstreamShortName ?? undefined, + })) + : [], commits: Array.isArray(data.commits) ? data.commits.map((commit: JsonRecord) => ({ hash: commit.hash, + shortHash: commit.shortHash ?? String(commit.hash ?? "").slice(0, 7), + parentHashes: Array.isArray(commit.parentHashes) ? commit.parentHashes : [], message: commit.subject, author: commit.authorName, email: commit.authorEmail, date: commit.date, + decorations: commit.decorations ?? "", })) : [], hasMore: Boolean(data.hasMore), From 5e02ad7bac6559e1e1a6f0344277f3e6e404f438 Mon Sep 17 00:00:00 2001 From: ybx <3046829330@qq.com> Date: Sun, 16 Aug 2026 17:10:55 +0800 Subject: [PATCH 11/13] feat(windows): expose Git Log from the footer --- .../windows-git-log-tool-window.png | Bin 0 -> 198702 bytes .../keymaps/commands/command-registry.ts | 8 +++++++ .../keymaps/commands/view-command-actions.ts | 10 +++++++++ .../defaults/default-keymaps.git-log.test.ts | 19 +++++++++++++++++ .../keymaps/defaults/default-keymaps.ts | 1 + .../layout/components/footer/footer.tsx | 20 ++++++++++++++++++ .../src/features/layout/config/item-order.ts | 1 + 7 files changed, 59 insertions(+) create mode 100644 docs/assets/screenshots/windows-git-log-tool-window.png create mode 100644 windows/tauri/src/features/keymaps/defaults/default-keymaps.git-log.test.ts diff --git a/docs/assets/screenshots/windows-git-log-tool-window.png b/docs/assets/screenshots/windows-git-log-tool-window.png new file mode 100644 index 0000000000000000000000000000000000000000..1159a12beb4d86b64f3cd39276b89e893671cb09 GIT binary patch literal 198702 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%0D{f7&%1*}D^%Fhh$dU#lMQ2nK; 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

^t&U-#r!%7J^Zm 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 { + test("binds the IntelliJ-compatible Alt+9 shortcut", () => { + expect(defaultKeymaps).toContainEqual({ + key: "alt+9", + command: "workbench.toggleGitLog", + source: "default", + }); + }); + + test("adds the Git button to an existing persisted footer order", () => { + expect( + normalizeItemOrder(["branch", "terminal", "diagnostics"], FOOTER_LEADING_ITEM_IDS), + ).toEqual(["branch", "terminal", "diagnostics", "gitLog"]); + }); +}); diff --git a/windows/tauri/src/features/keymaps/defaults/default-keymaps.ts b/windows/tauri/src/features/keymaps/defaults/default-keymaps.ts index 5e6cd0ddd..5118863f7 100644 --- a/windows/tauri/src/features/keymaps/defaults/default-keymaps.ts +++ b/windows/tauri/src/features/keymaps/defaults/default-keymaps.ts @@ -466,6 +466,7 @@ export const defaultKeymaps: Keybinding[] = [ command: "workbench.commandPalette", source: "default", }, + { key: "alt+9", command: "workbench.toggleGitLog", source: "default" }, { key: "cmd+r", command: "workbench.toggleAIChat", source: "default" }, { key: "cmd+shift+m", command: "workbench.toggleMinimap", source: "default" }, { diff --git a/windows/tauri/src/features/layout/components/footer/footer.tsx b/windows/tauri/src/features/layout/components/footer/footer.tsx index 2219431e8..acf625c44 100644 --- a/windows/tauri/src/features/layout/components/footer/footer.tsx +++ b/windows/tauri/src/features/layout/components/footer/footer.tsx @@ -18,6 +18,7 @@ import { orderChromeItems, type ChromeItem } from "@/features/layout/utils/chrom import { useFooterGitBranchItem } from "./footer-git-branch-item"; import { FooterTabControl } from "./footer-tab-control"; import { + ClockCounterClockwiseIcon, DatabaseIcon, TerminalWindowIcon, WarningIcon, @@ -55,6 +56,25 @@ const Footer = () => { ); const footerLeadingItemsSource: Array | null> = [ branchItem, + { + id: "gitLog", + label: "Git", + content: ( + { + const showingGitLog = !isBottomPaneVisible || bottomPaneActiveTab !== "gitLog"; + setBottomPaneActiveTab("gitLog"); + setIsBottomPaneVisible(showingGitLog); + }} + > + + Git + + ), + }, terminalEnabled ? { id: "terminal", diff --git a/windows/tauri/src/features/layout/config/item-order.ts b/windows/tauri/src/features/layout/config/item-order.ts index cb66ba020..78dd71730 100644 --- a/windows/tauri/src/features/layout/config/item-order.ts +++ b/windows/tauri/src/features/layout/config/item-order.ts @@ -9,6 +9,7 @@ export const SIDEBAR_ACTIVITY_ITEM_IDS = [ ] as const; export const FOOTER_LEADING_ITEM_IDS = [ "branch", + "gitLog", "terminal", "diagnostics", ] as const; From 1eb1616ae87500624df53131b1d10b6a6a0bb203 Mon Sep 17 00:00:00 2001 From: lick <2188718831@qq.com> Date: Sun, 16 Aug 2026 20:31:42 +0800 Subject: [PATCH 12/13] fix(windows): preserve Git integration recovery state --- .../git/api/git-integration-api.test.ts | 82 +++++++++++++++++++ .../features/git/api/git-integration-api.ts | 42 +++++----- .../git/components/git-branch-manager.tsx | 12 ++- .../git/components/git-commit-panel.tsx | 9 +- .../git/components/git-operation-banner.tsx | 2 +- .../git/hooks/use-git-data-controller.ts | 22 +++-- .../src/features/git/stores/git.store.test.ts | 35 ++++++++ .../src/features/git/stores/git.store.ts | 4 +- 8 files changed, 176 insertions(+), 32 deletions(-) create mode 100644 windows/tauri/src/features/git/api/git-integration-api.test.ts diff --git a/windows/tauri/src/features/git/api/git-integration-api.test.ts b/windows/tauri/src/features/git/api/git-integration-api.test.ts new file mode 100644 index 000000000..6731ea69b --- /dev/null +++ b/windows/tauri/src/features/git/api/git-integration-api.test.ts @@ -0,0 +1,82 @@ +import { beforeEach, describe, expect, mock, test } from "bun:test"; +import type { GitOperationState } from "../types/git.types"; + +const invoke = mock(async (_command: string, _args?: unknown): Promise => null); +const emitGitChanged = mock((_change: unknown) => {}); +const resolveRepositoryPathOrThrow = mock(async (repoPath: string) => repoPath); + +mock.module("@/platform/tauri-core", () => ({ invoke })); +mock.module("../events/git-events", () => ({ emitGitChanged })); +mock.module("./git-repo-api", () => ({ resolveRepositoryPathOrThrow })); + +const { getConflictMarkerPaths, getOperationState, mergeBranch, rebaseOntoBranch } = await import( + "./git-integration-api" +); + +const operationState = ( + kind: GitOperationState["kind"], + conflictedPaths: string[] = [], +): GitOperationState => ({ + kind, + reference: null, + conflictedPaths, + step: null, + total: null, +}); + +beforeEach(() => { + invoke.mockReset(); + emitGitChanged.mockReset(); + resolveRepositoryPathOrThrow.mockReset(); + resolveRepositoryPathOrThrow.mockImplementation(async (repoPath: string) => repoPath); +}); + +describe("Git integration state", () => { + test("reports a stopped rebase even when no conflicted paths remain", async () => { + invoke.mockImplementation(async (command: string) => { + if (command === "git_integration_preflight") { + return { blockingPaths: [], blocksEntirely: false }; + } + if (command === "git_rebase") throw new Error("rebase stopped"); + if (command === "git_operation_state") return operationState("rebase"); + return null; + }); + + await expect(rebaseOntoBranch("C:/repo", "main")).resolves.toEqual({ status: "stopped" }); + expect(emitGitChanged).toHaveBeenCalledWith({ + repoPath: "C:/repo", + scopes: ["working-tree", "history", "refs"], + source: "rebase-rejected", + }); + }); + + test("reports conflicted paths when a merge stops on conflicts", async () => { + invoke.mockImplementation(async (command: string) => { + if (command === "git_integration_preflight") { + return { blockingPaths: [], blocksEntirely: false }; + } + if (command === "git_merge") throw new Error("merge stopped"); + if (command === "git_operation_state") { + return operationState("merge", ["src/app.ts"]); + } + return null; + }); + + await expect(mergeBranch("C:/repo", "feature")).resolves.toEqual({ + status: "conflicts", + conflictedPaths: ["src/app.ts"], + }); + }); + + test("propagates operation state query failures", async () => { + invoke.mockRejectedValue(new Error("Core unavailable")); + + await expect(getOperationState("C:/repo")).rejects.toThrow("Core unavailable"); + }); + + test("propagates conflict marker query failures so commits fail closed", async () => { + invoke.mockRejectedValue(new Error("Core unavailable")); + + await expect(getConflictMarkerPaths("C:/repo")).rejects.toThrow("Core unavailable"); + }); +}); diff --git a/windows/tauri/src/features/git/api/git-integration-api.ts b/windows/tauri/src/features/git/api/git-integration-api.ts index ba528ae4d..2fea725ec 100644 --- a/windows/tauri/src/features/git/api/git-integration-api.ts +++ b/windows/tauri/src/features/git/api/git-integration-api.ts @@ -13,6 +13,7 @@ interface IntegrationPreflightResult { export type IntegrationOutcome = | { status: "clean" } | { status: "conflicts"; conflictedPaths: string[] } + | { status: "stopped" } | { status: "blocked"; blockingPaths: string[]; blocksEntirely: boolean } | { status: "error"; message: string }; @@ -37,26 +38,18 @@ const notifyOperationChanged = (repoPath: string, source: string) => { export const getOperationState = async ( repoPath: string, ): Promise => { - try { - const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); - return await tauriInvoke("git_operation_state", { - repoPath: resolvedRepoPath, - }); - } catch { - return null; - } + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + return tauriInvoke("git_operation_state", { + repoPath: resolvedRepoPath, + }); }; export const getConflictMarkerPaths = async (repoPath: string): Promise => { - try { - const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); - const result = await tauriInvoke<{ paths: string[] }>("git_conflict_markers", { - repoPath: resolvedRepoPath, - }); - return result.paths; - } catch { - return []; - } + const resolvedRepoPath = await resolveRepositoryPathOrThrow(repoPath); + const result = await tauriInvoke<{ paths: string[] }>("git_conflict_markers", { + repoPath: resolvedRepoPath, + }); + return result.paths; }; const runIntegration = async ( @@ -72,10 +65,17 @@ const runIntegration = async ( } catch (error) { // A conflict stop exits non-zero like a real failure; the authoritative // distinction is whether Git left an operation state behind. - const state = await getOperationState(repoPath); - if (state && state.kind === operation && state.conflictedPaths.length > 0) { - notifyOperationChanged(repoPath, `${operation}-conflicts`); - return { status: "conflicts", conflictedPaths: state.conflictedPaths }; + notifyOperationChanged(repoPath, `${operation}-rejected`); + try { + const state = await getOperationState(repoPath); + if (state?.kind === operation) { + if (state.conflictedPaths.length > 0) { + return { status: "conflicts", conflictedPaths: state.conflictedPaths }; + } + return { status: "stopped" }; + } + } catch (stateError) { + console.error(`Failed to read ${operation} state after Git rejected the operation:`, stateError); } return { status: "error", message: errorMessage(error) }; } diff --git a/windows/tauri/src/features/git/components/git-branch-manager.tsx b/windows/tauri/src/features/git/components/git-branch-manager.tsx index 9812fc05a..dd8e4d5c7 100644 --- a/windows/tauri/src/features/git/components/git-branch-manager.tsx +++ b/windows/tauri/src/features/git/components/git-branch-manager.tsx @@ -362,6 +362,12 @@ const GitBranchManager = ({ type: "warning", duration: 6000, }); + } else if (outcome.status === "stopped") { + showToast({ + message: `${branchName} stopped before completion. Continue, skip, or abort from the changes list.`, + type: "warning", + duration: 6000, + }); } else if (outcome.status === "blocked") { const listed = outcome.blockingPaths.slice(0, 3).join(", "); const remaining = outcome.blockingPaths.length - Math.min(outcome.blockingPaths.length, 3); @@ -398,7 +404,11 @@ const GitBranchManager = ({ ? await mergeBranch(repoPath, branchName) : await rebaseOntoBranch(repoPath, branchName); reportIntegrationOutcome(branchName, outcome); - if (outcome.status === "clean" || outcome.status === "conflicts") { + if ( + outcome.status === "clean" || + outcome.status === "conflicts" || + outcome.status === "stopped" + ) { onBranchChange?.(); } } catch (error) { diff --git a/windows/tauri/src/features/git/components/git-commit-panel.tsx b/windows/tauri/src/features/git/components/git-commit-panel.tsx index 089c5fbec..a080e6bce 100644 --- a/windows/tauri/src/features/git/components/git-commit-panel.tsx +++ b/windows/tauri/src/features/git/components/git-commit-panel.tsx @@ -297,7 +297,14 @@ const GitCommitPanel = ({ return; } - const markerPaths = await getConflictMarkerPaths(repoPath); + let markerPaths: string[]; + try { + markerPaths = await getConflictMarkerPaths(repoPath); + } catch (markerError) { + console.error("Failed to check staged files for conflict markers:", markerError); + setError("Unable to verify conflict markers. Retry before committing."); + return; + } if (markerPaths.length > 0) { setError(`Conflict markers remain in: ${markerPaths.join(", ")}`); return; diff --git a/windows/tauri/src/features/git/components/git-operation-banner.tsx b/windows/tauri/src/features/git/components/git-operation-banner.tsx index 6ecfbcfc0..96a0153e7 100644 --- a/windows/tauri/src/features/git/components/git-operation-banner.tsx +++ b/windows/tauri/src/features/git/components/git-operation-banner.tsx @@ -37,7 +37,7 @@ interface GitOperationBannerProps { */ const GitOperationBanner = ({ repoPath }: GitOperationBannerProps) => { const operation = useGitStore((state) => state.operationState); - + const [isResolving, setIsResolving] = useState(false); const resolve = useCallback( diff --git a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts index 71b750040..c891d5e88 100644 --- a/windows/tauri/src/features/git/hooks/use-git-data-controller.ts +++ b/windows/tauri/src/features/git/hooks/use-git-data-controller.ts @@ -41,12 +41,17 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl gitActions.setIsLoadingGitData(true); try { - const [status, history, branches, stashes, operationState] = await Promise.all([ + const [status, history, branches, stashes, operationStateResult] = await Promise.all([ getGitStatus(repoPath), getGitHistory(repoPath, 50), getBranches(repoPath), getStashes(repoPath), - getOperationState(repoPath), + getOperationState(repoPath) + .then((value) => ({ ok: true as const, value })) + .catch((error) => { + console.error("Failed to load Git operation state:", error); + return { ok: false as const }; + }), ]); if ( @@ -62,7 +67,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl hasMoreCommits: history?.hasMore ?? false, branches, stashes, - operationState, + operationState: operationStateResult.ok ? operationStateResult.value : null, repoPath, }); } catch (error) { @@ -94,14 +99,19 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl refreshAll || scopes.includes("refs") || scopes.includes("repository"); const shouldRefreshStashes = refreshAll || scopes.includes("stashes") || scopes.includes("repository"); - const [status, branches, stashes, history, operationState] = await Promise.all([ + const [status, branches, stashes, history, operationStateResult] = await Promise.all([ getGitStatus(repoPath), shouldRefreshRefs ? getBranches(repoPath) : Promise.resolve(undefined), shouldRefreshStashes ? getStashes(repoPath) : Promise.resolve(undefined), shouldRefreshHistory ? getGitHistory(repoPath, 50) : Promise.resolve(undefined), // Operation state rides along on every refresh: staging a file or // an external Git command can end a conflict at any moment. - getOperationState(repoPath), + getOperationState(repoPath) + .then((value) => ({ ok: true as const, value })) + .catch((error) => { + console.error("Failed to refresh Git operation state:", error); + return { ok: false as const }; + }), ]); if ( @@ -116,7 +126,7 @@ export function useGitDataController({ workspacePath, isActive }: GitDataControl branches, commits: history?.commits, hasMoreCommits: history?.hasMore, - operationState, + operationState: operationStateResult.ok ? operationStateResult.value : undefined, repoPath, }); diff --git a/windows/tauri/src/features/git/stores/git.store.test.ts b/windows/tauri/src/features/git/stores/git.store.test.ts index e625b746d..e62b7dff0 100644 --- a/windows/tauri/src/features/git/stores/git.store.test.ts +++ b/windows/tauri/src/features/git/stores/git.store.test.ts @@ -98,3 +98,38 @@ describe("Git history pagination", () => { expect(store.getState().isLoadingMoreCommits).toBe(false); }); }); + +describe("Git operation state refresh", () => { + test("keeps the last operation state when a refresh omits a failed query", () => { + const store = createGitStore(); + store.getState().actions.prepareRepositoryLoad("C:/repo"); + store.getState().actions.loadFreshGitData({ + gitStatus: null, + commits: [], + hasMoreCommits: false, + branches: [], + stashes: [], + operationState: { + kind: "rebase", + reference: "refs/heads/main", + step: 2, + total: 4, + conflictedPaths: [], + }, + repoPath: "C:/repo", + }); + + store.getState().actions.refreshGitData({ + gitStatus: null, + repoPath: "C:/repo", + }); + + expect(store.getState().operationState).toEqual({ + kind: "rebase", + reference: "refs/heads/main", + step: 2, + total: 4, + conflictedPaths: [], + }); + }); +}); diff --git a/windows/tauri/src/features/git/stores/git.store.ts b/windows/tauri/src/features/git/stores/git.store.ts index 0c88d6c6b..fdd324fc4 100644 --- a/windows/tauri/src/features/git/stores/git.store.ts +++ b/windows/tauri/src/features/git/stores/git.store.ts @@ -35,7 +35,7 @@ interface GitState { branches?: string[]; commits?: GitCommit[]; hasMoreCommits?: boolean; - operationState: GitOperationState | null; + operationState?: GitOperationState | null; repoPath: string; }) => void; refreshWorkspaceGitStatus: (repoPath: string) => Promise; @@ -118,7 +118,7 @@ export const createGitStore = () => set({ gitStatus, - operationState, + ...(operationState !== undefined ? { operationState } : {}), ...(branches ? { branches } : {}), ...(commits ? { From 0992bfb7ffa34fc93333ce22e53e8b0b16425edd Mon Sep 17 00:00:00 2001 From: lick <2188718831@qq.com> Date: Sun, 16 Aug 2026 20:36:03 +0800 Subject: [PATCH 13/13] fix(windows): refresh Git Log after repository changes --- .../git/hooks/use-git-log-controller.ts | 18 +++++++++++ .../git/utils/git-log-refresh.test.ts | 32 +++++++++++++++++++ .../src/features/git/utils/git-log-refresh.ts | 8 +++++ 3 files changed, 58 insertions(+) create mode 100644 windows/tauri/src/features/git/utils/git-log-refresh.test.ts create mode 100644 windows/tauri/src/features/git/utils/git-log-refresh.ts diff --git a/windows/tauri/src/features/git/hooks/use-git-log-controller.ts b/windows/tauri/src/features/git/hooks/use-git-log-controller.ts index 3e96c7907..be2e1b979 100644 --- a/windows/tauri/src/features/git/hooks/use-git-log-controller.ts +++ b/windows/tauri/src/features/git/hooks/use-git-log-controller.ts @@ -1,6 +1,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { getGitHistory } from "../api/git-commits-api"; +import { subscribeToGitChanges } from "../events/git-events"; import type { GitHistorySnapshot, GitReference } from "../types/git.types"; +import { shouldRefreshGitLogForChange } from "../utils/git-log-refresh"; type GitLogLoadState = "idle" | "loading" | "ready" | "failed"; @@ -95,6 +97,22 @@ export function useGitLogController(repoPath: string | null) { return load({ reference: selectedReferenceRef.current, limit }); }, [load]); + useEffect(() => { + if (!repoPath) return; + + let timeoutId: ReturnType | null = null; + const unsubscribe = subscribeToGitChanges((change) => { + if (!shouldRefreshGitLogForChange(change, repoPath)) return; + if (timeoutId) clearTimeout(timeoutId); + timeoutId = setTimeout(() => void refresh(), 100); + }); + + return () => { + unsubscribe(); + if (timeoutId) clearTimeout(timeoutId); + }; + }, [refresh, repoPath]); + const loadMore = useCallback(() => { const currentHistory = historyRef.current; if (!currentHistory.hasMore || isLoadingMore) return Promise.resolve(); diff --git a/windows/tauri/src/features/git/utils/git-log-refresh.test.ts b/windows/tauri/src/features/git/utils/git-log-refresh.test.ts new file mode 100644 index 000000000..cbdab94f4 --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-log-refresh.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "bun:test"; +import { shouldRefreshGitLogForChange } from "./git-log-refresh"; + +describe("Git Log refresh events", () => { + test("refreshes for history, refs, and repository changes", () => { + const repoPath = "C:/work/project"; + + expect(shouldRefreshGitLogForChange({ repoPath, scopes: ["history"] }, repoPath)).toBe(true); + expect(shouldRefreshGitLogForChange({ repoPath, scopes: ["refs"] }, repoPath)).toBe(true); + expect(shouldRefreshGitLogForChange({ repoPath, scopes: ["repository"] }, repoPath)).toBe(true); + }); + + test("ignores working-tree-only and unrelated repository changes", () => { + const repoPath = "C:/work/project"; + + expect(shouldRefreshGitLogForChange({ repoPath, scopes: ["working-tree"] }, repoPath)).toBe( + false, + ); + expect( + shouldRefreshGitLogForChange( + { repoPath: "C:/work/other", scopes: ["history"] }, + repoPath, + ), + ).toBe(false); + }); + + test("refreshes conservatively when an event has no scopes", () => { + expect(shouldRefreshGitLogForChange({ repoPath: "C:/work/project" }, "C:/work/project")).toBe( + true, + ); + }); +}); diff --git a/windows/tauri/src/features/git/utils/git-log-refresh.ts b/windows/tauri/src/features/git/utils/git-log-refresh.ts new file mode 100644 index 000000000..f7ef0a0ea --- /dev/null +++ b/windows/tauri/src/features/git/utils/git-log-refresh.ts @@ -0,0 +1,8 @@ +import { isGitChangeRelevant, type GitChange } from "../events/git-events"; + +const GIT_LOG_SCOPES = new Set(["history", "refs", "repository"]); + +export function shouldRefreshGitLogForChange(change: GitChange, repoPath: string): boolean { + if (!isGitChangeRelevant(change, repoPath)) return false; + return !change.scopes?.length || change.scopes.some((scope) => GIT_LOG_SCOPES.has(scope)); +}