diff --git a/src-tauri/src/git.rs b/src-tauri/src/git.rs index 2e2486c..b14c74a 100644 --- a/src-tauri/src/git.rs +++ b/src-tauri/src/git.rs @@ -996,6 +996,8 @@ fn commits_for_repo(repo: &Path, limit: Option) -> Result, S repo, [ "log", + "--all", + "--topo-order", "--decorate=short", "--name-status", "-M", @@ -3869,6 +3871,33 @@ mod tests { assert!(comparison.patch.contains("+original")); } + #[test] + fn commits_for_repo_includes_all_branch_tips_for_graph() { + let repo = init_temp_repo("commits_all_branches"); + commit_initial_file(&repo.path); + let base_branch = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]); + + run_git_test(&repo.path, ["checkout", "-q", "-b", "feature/graph"]); + fs::write(repo.path.join("feature.txt"), "feature\n") + .expect("feature file should be written"); + run_git_test(&repo.path, ["add", "feature.txt"]); + run_git_test(&repo.path, ["commit", "-q", "-m", "feature graph"]); + let feature_commit = git_output_test(&repo.path, ["rev-parse", "HEAD"]); + + run_git_test(&repo.path, ["checkout", "-q", base_branch.as_str()]); + fs::write(repo.path.join("main.txt"), "main\n").expect("main file should be written"); + run_git_test(&repo.path, ["add", "main.txt"]); + run_git_test(&repo.path, ["commit", "-q", "-m", "main graph"]); + let main_commit = git_output_test(&repo.path, ["rev-parse", "HEAD"]); + + let commits = commits_for_repo(&repo.path, Some(10)).expect("commits should load"); + + assert!(commits.iter().any(|commit| commit.hash == main_commit)); + assert!(commits.iter().any(|commit| { + commit.hash == feature_commit && commit.refs.iter().any(|r| r.contains("feature/graph")) + })); + } + #[test] #[cfg_attr( windows, diff --git a/src/App.svelte b/src/App.svelte index 206f403..ef27d3f 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -117,9 +117,13 @@ const RECENT_REPOS_KEY = "gitlite.recentRepos.v1"; const AI_SETTINGS_KEY = "gitlite.aiSettings.v1"; const COMMIT_PANEL_HEIGHT_KEY = "gitlite.commitPanelHeight.v1"; + const HISTORY_ASIDE_WIDTH_KEY = "gitlite.historyAsideWidth.v1"; const COMMIT_PANEL_DEFAULT_HEIGHT = 220; const COMMIT_PANEL_MIN_HEIGHT = COMMIT_PANEL_DEFAULT_HEIGHT; const COMMIT_PANEL_MAX_HEIGHT = 640; + const HISTORY_ASIDE_DEFAULT_WIDTH = 620; + const HISTORY_ASIDE_MIN_WIDTH = 560; + const HISTORY_ASIDE_MAX_WIDTH = 920; // ── State ────────────────────────────────────────────────────────────────── @@ -207,6 +211,10 @@ let resizingCommitPanel = false; let resizeStartY = 0; let resizeStartHeight = 0; + let historyAsideWidth = loadHistoryAsideWidth(); + let resizingHistoryAside = false; + let historyResizeStartX = 0; + let historyResizeStartWidth = 0; // ── Derived ──────────────────────────────────────────────────────────────── @@ -226,6 +234,7 @@ : ""; $: canCompare = hasRepository && compareFrom.length > 0 && compareTo.length > 0 && compareFrom !== compareTo && !isBusy; $: localBranches = branches.filter((b) => !b.remote); + $: localBranchNames = localBranches.map((b) => b.name); $: remoteBranches = branches.filter((b) => b.remote); $: repoSearchTerm = repoSearch.trim().toLowerCase(); $: openRepoRows = repoTabs.filter(repoMatchesSearch); @@ -625,6 +634,28 @@ } } + function clampHistoryAsideWidth(value: number): number { + return Math.min(HISTORY_ASIDE_MAX_WIDTH, Math.max(HISTORY_ASIDE_MIN_WIDTH, Math.round(value))); + } + + function loadHistoryAsideWidth(): number { + try { + const stored = Number(localStorage.getItem(HISTORY_ASIDE_WIDTH_KEY)); + if (Number.isFinite(stored) && stored > 0) return clampHistoryAsideWidth(stored); + } catch { + // Fall through to the default below. + } + return HISTORY_ASIDE_DEFAULT_WIDTH; + } + + function persistHistoryAsideWidth(value: number) { + try { + localStorage.setItem(HISTORY_ASIDE_WIDTH_KEY, String(value)); + } catch { + // Local storage is best-effort only; resizing must keep working without it. + } + } + function startCommitPanelResize(event: PointerEvent) { event.preventDefault(); resizingCommitPanel = true; @@ -653,6 +684,34 @@ persistCommitPanelHeight(commitPanelHeight); } + function startHistoryAsideResize(event: PointerEvent) { + event.preventDefault(); + resizingHistoryAside = true; + historyResizeStartX = event.clientX; + historyResizeStartWidth = historyAsideWidth; + (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId); + } + + function onHistoryAsideResizeMove(event: PointerEvent) { + if (!resizingHistoryAside) return; + historyAsideWidth = clampHistoryAsideWidth(historyResizeStartWidth + (historyResizeStartX - event.clientX)); + } + + function endHistoryAsideResize(event: PointerEvent) { + if (!resizingHistoryAside) return; + resizingHistoryAside = false; + persistHistoryAsideWidth(historyAsideWidth); + const target = event.currentTarget as HTMLElement; + if (target.hasPointerCapture(event.pointerId)) target.releasePointerCapture(event.pointerId); + } + + function onHistoryAsideResizeKeydown(event: KeyboardEvent) { + if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return; + event.preventDefault(); + historyAsideWidth = clampHistoryAsideWidth(historyAsideWidth + (event.key === "ArrowLeft" ? 24 : -24)); + persistHistoryAsideWidth(historyAsideWidth); + } + function rememberRecentRepo(path: string) { recentRepoPaths = uniqueRepoPaths([path, ...recentRepoPaths]).slice(0, 40); persistRepoLists(); @@ -1945,7 +2004,7 @@ {:else} -
+