feat(git): add upstream tracking and local-only branch UI

This release adds upstream tracking for branches and a local-only
indicator in the UI.
The Git integration now exposes upstreams and supports publish flows.
This enables publishing and remote-tracking configuration.

- Introduces upstream tracking and local-only branch UI markers
- Updates toolbar to reflect publish-local state and status indicators
- Bumps version to 2026.8.3 and updates changelog
This commit is contained in:
Christoph Brandau
2026-08-13 19:32:26 +02:00
parent f43fe00873
commit b6b9964a93
11 changed files with 420 additions and 45 deletions
+31 -6
View File
@@ -4,7 +4,7 @@
import { getCurrentWindow } from "@tauri-apps/api/window";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater";
import { AlertCircle, Cherry, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte";
import { AlertCircle, Cherry, CloudOff, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte";
import { beginFrontendShutdown } from "./lib/telemetry";
import TitleBar from "./lib/TitleBar.svelte";
@@ -506,7 +506,11 @@
$: canCompare = hasRepository && compareFrom.length > 0 && compareTo.length > 0 && compareFrom !== compareTo && !isBusy;
$: localBranches = branches.filter((b) => !b.remote);
$: localBranchNames = localBranches.map((b) => b.name);
$: localBranchUpstreams = Object.fromEntries(
localBranches.flatMap((branch) => branch.upstream ? [[branch.name, branch.upstream]] : []),
) as Record<string, string>;
$: remoteBranches = branches.filter((b) => b.remote);
$: currentBranchIsLocalOnly = Boolean(status?.current_branch) && !status?.upstream;
$: repoSearchTerm = repoSearch.trim().toLowerCase();
$: openRepoRows = repoTabs.filter((repo) => repoMatchesSearch(repo, repoSearchTerm));
$: recentRepoRows = recentRepoPaths
@@ -4445,6 +4449,7 @@
{operation}
ahead={status?.ahead ?? 0}
behind={status?.behind ?? 0}
localOnly={currentBranchIsLocalOnly}
language={appLanguage}
editorName={editorToolName}
terminalName={terminalToolName}
@@ -4896,9 +4901,22 @@
{/if}
</div>
<div class="sync-stats" aria-label="Sync state">
{#if status?.upstream}<span title="Upstream">{status.upstream}</span>{/if}
<strong>{status?.ahead ?? 0} ahead</strong>
<strong>{status?.behind ?? 0} behind</strong>
{#if currentBranchIsLocalOnly}
<span
class="sync-local-only"
title={appLanguage === "de"
? "Dieser Branch existiert nur auf diesem Computer. Veröffentlichen richtet den Remote-Branch ein."
: "This branch exists only on this computer. Publish configures its remote branch."}
>
<CloudOff size={11} aria-hidden="true" />
<strong>{appLanguage === "de" ? "Nur lokal" : "Local only"}</strong>
<small>{appLanguage === "de" ? "noch nicht veröffentlicht" : "not published yet"}</small>
</span>
{:else}
{#if status?.upstream}<span title="Upstream">{status.upstream}</span>{/if}
<strong>{status?.ahead ?? 0} ahead</strong>
<strong>{status?.behind ?? 0} behind</strong>
{/if}
</div>
</div>
@@ -4991,6 +5009,7 @@
{commits}
{selectedCommitHash}
{localBranchNames}
{localBranchUpstreams}
remoteBranchNames={remoteBranches.map((branch) => branch.name)}
activeBranch={status?.current_branch ?? ""}
activeUpstream={status?.upstream ?? ""}
@@ -5027,8 +5046,14 @@
<span class="workspace-status-spacer"></span>
{#if workspaceActive}
<span class="workspace-branch"><GitBranch size={12} aria-hidden="true" />{status?.current_branch ?? "No branch"}</span>
<span class="ahead">↑ {status?.ahead ?? 0}</span>
<span class="behind">↓ {status?.behind ?? 0}</span>
{#if currentBranchIsLocalOnly}
<span class="workspace-local-only" title={appLanguage === "de" ? "Branch noch nicht veröffentlicht" : "Branch not published yet"}>
<CloudOff size={11} aria-hidden="true" />{appLanguage === "de" ? "Nur lokal" : "Local only"}
</span>
{:else}
<span class="ahead">↑ {status?.ahead ?? 0}</span>
<span class="behind">↓ {status?.behind ?? 0}</span>
{/if}
<span class:active={autoRefreshEnabled} class="workspace-auto">Auto <i aria-hidden="true"></i></span>
{/if}
{#if appVersion}<span class="app-version" title={`Gitty version ${appVersion}`}>Gitty v{appVersion}</span>{/if}