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:
@@ -3,6 +3,7 @@
|
||||
ChevronDown,
|
||||
Code2,
|
||||
CloudDownload,
|
||||
CloudOff,
|
||||
Download,
|
||||
FolderOpen,
|
||||
GitCompare,
|
||||
@@ -21,6 +22,7 @@
|
||||
export let operation: string = "";
|
||||
export let ahead: number = 0;
|
||||
export let behind: number = 0;
|
||||
export let localOnly: boolean = false;
|
||||
export let language: "en" | "de" = "en";
|
||||
export let editorName: string = "Editor";
|
||||
export let terminalName: string = "Terminal";
|
||||
@@ -45,6 +47,12 @@
|
||||
let toolbarElement: HTMLDivElement;
|
||||
|
||||
$: isGerman = language === "de";
|
||||
$: pushLabel = localOnly ? (isGerman ? "Veröffentlichen" : "Publish") : "Push";
|
||||
$: pushTitle = localOnly
|
||||
? (isGerman
|
||||
? "Dieser Branch existiert nur lokal. Veröffentlichen erstellt den Remote-Branch und richtet das Tracking ein."
|
||||
: "This branch exists only locally. Publish creates the remote branch and configures tracking.")
|
||||
: "Push";
|
||||
|
||||
function runHistoryAction(action: () => void) {
|
||||
historyOpen = false;
|
||||
@@ -103,10 +111,13 @@
|
||||
|
||||
<button
|
||||
class="repo-action sync-primary"
|
||||
class:publish-local={localOnly}
|
||||
onclick={onPush}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Push"
|
||||
aria-label={ahead > 0
|
||||
title={pushTitle}
|
||||
aria-label={localOnly
|
||||
? pushTitle
|
||||
: ahead > 0
|
||||
? `Push, ${ahead} ${isGerman ? (ahead === 1 ? "lokaler Commit voraus" : "lokale Commits voraus") : (ahead === 1 ? "commit ahead" : "commits ahead")}`
|
||||
: "Push"}
|
||||
>
|
||||
@@ -115,8 +126,12 @@
|
||||
{:else}
|
||||
<Upload size={15} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="repo-action-label">Push</span>
|
||||
{#if ahead > 0}<span class="repo-action-count ahead">↑{ahead}</span>{/if}
|
||||
<span class="repo-action-label">{pushLabel}</span>
|
||||
{#if localOnly}
|
||||
<span class="repo-action-local-marker"><CloudOff size={9} aria-hidden="true" />{isGerman ? "NUR LOKAL" : "LOCAL"}</span>
|
||||
{:else if ahead > 0}
|
||||
<span class="repo-action-count ahead">↑{ahead}</span>
|
||||
{/if}
|
||||
</button>
|
||||
<div class="repo-history-wrap">
|
||||
<button class="repo-action" type="button" onclick={() => { syncOpen = !syncOpen; historyOpen = false; }} disabled={!hasRepository || isBusy} aria-label={isGerman ? "Sync-Optionen" : "Sync options"} aria-haspopup="menu">
|
||||
|
||||
@@ -1361,6 +1361,45 @@
|
||||
label: "Neu in Gitty",
|
||||
description: "Änderungen seit der letzten veröffentlichten Version und wichtige Neuerungen früherer Releases.",
|
||||
sections: [
|
||||
{
|
||||
id: "changelog-2026-8-3",
|
||||
title: "Version 2026.8.3",
|
||||
summary: "Dieses Release verbindet Gitty enger mit deinen Entwicklungswerkzeugen und macht Branches, Historie und Vergleiche deutlich leistungsfähiger.",
|
||||
steps: [
|
||||
"Externe Tools: Editor, Diff-Tool, Merge-Tool, Terminal und Dateimanager lassen sich in den neu gestalteten Einstellungen erkennen, auswählen und individuell konfigurieren.",
|
||||
"VS Code, JetBrains-IDEs, Beyond Compare und weitere unterstützte Programme werden in einem eigenen Fenster geöffnet; dokumentierte Ergebnis-Codes werden beim Schließen korrekt behandelt.",
|
||||
"Git Notes: Commits erhalten lokale Notizen, ohne ihre Historie umzuschreiben. Notizen lassen sich bearbeiten, löschen sowie gezielt vom Remote abrufen oder dorthin übertragen.",
|
||||
"Vollständiger Branch-Vergleich: Lokale Branches, Remote-Branches und Commits können direkt ausgewählt und dateiweise im Side-by-Side-Diff verglichen werden.",
|
||||
"Remote-Branches lassen sich im Kontextmenü sicher umbenennen. Gitty schützt dabei vorhandene Ziel-Branches und zwischenzeitlich geänderte Remote-Stände.",
|
||||
"Der überarbeitete Commit-Graph zeigt Branches kompakter, reduziert überladene Commit-Zeilen und blendet zusätzliche Flag-Details beim Darüberfahren ein.",
|
||||
"Noch nicht veröffentlichte Branches sind als „Nur lokal“ erkennbar – in der Werkzeugleiste, Repository-Übersicht, Statuszeile und direkt an der Branch-Flag. Die erste Push-Aktion heißt passend „Veröffentlichen“.",
|
||||
"Branch-Sichtbarkeit, feinere Graph-Verbindungen und ein kleineres Mindestmaß des Verlaufsbereichs verbessern die Übersicht bei großen Repositories.",
|
||||
"Die neue Befehlspalette öffnet Aktionen, Dateien und Commits schneller; asynchrone Git-Befehle halten Gitty auch bei langsameren Operationen reaktionsfähig.",
|
||||
],
|
||||
note: "Der Branch-Vergleich zeigt die vollständig festgeschriebenen Zustände der beiden Branch-Spitzen. Nicht commitete Änderungen im Arbeitsverzeichnis sind nicht enthalten.",
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-8-2",
|
||||
title: "Version 2026.8.2",
|
||||
summary: "Dieses Release stabilisiert die Darstellung komplexer Verläufe und verbessert die Veröffentlichung neuer Gitty-Versionen.",
|
||||
steps: [
|
||||
"Branch-Farben bleiben über Eltern-Lanes hinweg stabil, sodass sich Linien in längeren und verzweigten Historien leichter verfolgen lassen.",
|
||||
"Release-Artefakte werden automatisch und ohne doppelte Dateien an das passende Gitea-Release angehängt.",
|
||||
"Beim Beenden der Anwendung wird die Telemetrie zuverlässiger abgeschlossen.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-8-1",
|
||||
title: "Version 2026.8.1",
|
||||
summary: "Dieses Release macht große Commit-Verläufe und die Historie einzelner Dateien leichter zugänglich und verbessert die Paketverteilung.",
|
||||
steps: [
|
||||
"Die Commit-Historie lädt ältere Einträge seitenweise nach und ist nicht mehr auf die erste Ergebnismenge begrenzt.",
|
||||
"Die Dateihistorie öffnet sich aus dem Explorer-Kontextmenü in einem eigenen, größeren Dialog statt in einem dauerhaft belegten Seitenbereich.",
|
||||
"Dialoge reagieren konsistenter auf die Escape-Taste.",
|
||||
"Windows- und Ubuntu-Releases sowie der AUR-Paketablauf wurden erweitert und robuster gemacht.",
|
||||
"Die Arch-Linux-Anleitung verwendet jetzt das AUR-Paket gitty-desktop; SSH-Einrichtung, Zeitlimits und Wiederholungsversuche wurden verbessert.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-07-22",
|
||||
title: "Version 2026.07.22",
|
||||
@@ -1411,6 +1450,45 @@
|
||||
label: "What's new",
|
||||
description: "Changes since the latest published version and notable additions from earlier releases.",
|
||||
sections: [
|
||||
{
|
||||
id: "changelog-2026-8-3",
|
||||
title: "Version 2026.8.3",
|
||||
summary: "This release connects Gitty more closely with your development tools and makes branches, history, and comparisons substantially more capable.",
|
||||
steps: [
|
||||
"External tools: editors, diff tools, merge tools, terminals, and file managers can be detected, selected, and customized in the redesigned settings.",
|
||||
"VS Code, JetBrains IDEs, Beyond Compare, and other supported applications open in a separate window; documented result codes are handled correctly when they close.",
|
||||
"Git Notes: attach local notes to commits without rewriting history. Notes can be edited, deleted, fetched from a remote, or pushed explicitly.",
|
||||
"Complete branch comparison: choose local branches, remote branches, or commits and inspect every changed file in a side-by-side diff.",
|
||||
"Remote branches can be renamed safely from the context menu. Gitty protects existing destination branches and remote branches that changed after the last fetch.",
|
||||
"The redesigned commit graph presents branches more compactly, reduces crowded commit rows, and reveals additional flag details on hover.",
|
||||
"Unpublished branches are clearly marked as Local only in the toolbar, repository summary, status bar, and on the graph flag. Their first push is labeled Publish.",
|
||||
"Branch visibility controls, refined graph connectors, and a smaller minimum history width improve navigation in large repositories.",
|
||||
"The new command palette opens actions, files, and commits faster; asynchronous Git commands keep Gitty responsive during slower operations.",
|
||||
],
|
||||
note: "Branch comparison uses the fully committed state at each branch tip. Uncommitted working-tree changes are not included.",
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-8-2",
|
||||
title: "Version 2026.8.2",
|
||||
summary: "This release stabilizes complex history rendering and improves publication of new Gitty versions.",
|
||||
steps: [
|
||||
"Branch colors remain stable across parent lanes, making longer and branching histories easier to follow.",
|
||||
"Release artifacts are attached to the matching Gitea release automatically without uploading duplicates.",
|
||||
"Telemetry cleanup completes more reliably while the application is shutting down.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-8-1",
|
||||
title: "Version 2026.8.1",
|
||||
summary: "This release makes large commit histories and individual file histories easier to access and improves package distribution.",
|
||||
steps: [
|
||||
"Commit history loads older entries page by page instead of stopping after the initial result set.",
|
||||
"File history opens from the explorer context menu in a dedicated larger dialog instead of occupying a permanent workspace panel.",
|
||||
"Dialogs respond more consistently to the Escape key.",
|
||||
"Windows and Ubuntu publishing plus the AUR package workflow were expanded and made more robust.",
|
||||
"The Arch Linux guide now uses the gitty-desktop AUR package; SSH setup, timeouts, and retry handling were improved.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "changelog-2026-07-22",
|
||||
title: "Version 2026.07.22",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Check, Cherry, ChevronDown, ChevronRight, EllipsisVertical, GitBranch, GitMerge, LoaderCircle, RotateCcw, StickyNote, Tag, X } from "@lucide/svelte";
|
||||
import { Check, Cherry, ChevronDown, ChevronRight, CloudOff, EllipsisVertical, GitBranch, GitMerge, LoaderCircle, RotateCcw, StickyNote, Tag, X } from "@lucide/svelte";
|
||||
import type { FileStatusKind, GitCommit, GitCommitFile } from "../types";
|
||||
|
||||
interface GraphSegment {
|
||||
@@ -30,6 +30,7 @@
|
||||
kind: CommitRefKind;
|
||||
current: boolean;
|
||||
trackedRemote: string;
|
||||
localOnly: boolean;
|
||||
representedBranches: string[];
|
||||
}
|
||||
|
||||
@@ -57,6 +58,7 @@
|
||||
interface Props {
|
||||
commits: GitCommit[];
|
||||
localBranchNames: string[];
|
||||
localBranchUpstreams: Record<string, string>;
|
||||
remoteBranchNames: string[];
|
||||
activeBranch: string;
|
||||
activeUpstream: string;
|
||||
@@ -84,6 +86,7 @@
|
||||
let {
|
||||
commits = [],
|
||||
localBranchNames = [],
|
||||
localBranchUpstreams = {},
|
||||
remoteBranchNames = [],
|
||||
activeBranch = "",
|
||||
activeUpstream = "",
|
||||
@@ -541,20 +544,17 @@
|
||||
return !localBranchNameSet.has(label) && /(?:^|\/)HEAD(?:\s*->|$)/.test(ref);
|
||||
}
|
||||
|
||||
function branchNameWithoutRemote(branch: string): string {
|
||||
const slash = branch.indexOf("/");
|
||||
return slash === -1 ? branch : branch.slice(slash + 1);
|
||||
}
|
||||
|
||||
function remoteName(branch: string): string {
|
||||
return branch.split("/", 1)[0] ?? branch;
|
||||
}
|
||||
|
||||
function configuredUpstreamForBranch(local: string): string {
|
||||
return localBranchUpstreams[local] ?? (local === activeBranch ? activeUpstream : "");
|
||||
}
|
||||
|
||||
function matchingRemoteBranch(local: string, remoteBranches: string[]): string {
|
||||
if (local === activeBranch && activeUpstream && remoteBranches.includes(activeUpstream)) {
|
||||
return activeUpstream;
|
||||
}
|
||||
return remoteBranches.find((remote) => branchNameWithoutRemote(remote) === local) ?? "";
|
||||
const configuredUpstream = configuredUpstreamForBranch(local);
|
||||
return configuredUpstream && remoteBranches.includes(configuredUpstream) ? configuredUpstream : "";
|
||||
}
|
||||
|
||||
function compareBranchDecorations(left: CommitBranchDecoration, right: CommitBranchDecoration): number {
|
||||
@@ -594,6 +594,7 @@
|
||||
|
||||
const remainingRemote = [...remote];
|
||||
const branches: CommitBranchDecoration[] = [...local].map((label) => {
|
||||
const configuredUpstream = configuredUpstreamForBranch(label);
|
||||
const trackedRemote = matchingRemoteBranch(label, remainingRemote);
|
||||
if (trackedRemote) remainingRemote.splice(remainingRemote.indexOf(trackedRemote), 1);
|
||||
return {
|
||||
@@ -601,6 +602,7 @@
|
||||
kind: "local",
|
||||
current: label === activeBranch,
|
||||
trackedRemote,
|
||||
localOnly: !configuredUpstream,
|
||||
representedBranches: trackedRemote ? [label, trackedRemote] : [label],
|
||||
};
|
||||
});
|
||||
@@ -611,6 +613,7 @@
|
||||
kind: "head",
|
||||
current: true,
|
||||
trackedRemote: "",
|
||||
localOnly: false,
|
||||
representedBranches: [],
|
||||
});
|
||||
}
|
||||
@@ -620,6 +623,7 @@
|
||||
kind: "remote" as const,
|
||||
current: false,
|
||||
trackedRemote: "",
|
||||
localOnly: false,
|
||||
representedBranches: [label],
|
||||
})));
|
||||
|
||||
@@ -657,6 +661,7 @@
|
||||
}
|
||||
|
||||
function branchDecorationTitle(branch: CommitBranchDecoration): string {
|
||||
if (branch.localOnly) return `${branch.label} · Local only — not published yet`;
|
||||
if (branch.trackedRemote) return `${branch.label} · up to date with ${branch.trackedRemote}`;
|
||||
const status = branchStatusLabel(branch);
|
||||
if (status) return `${branch.label} · ${status}`;
|
||||
@@ -851,19 +856,26 @@
|
||||
<div class="commit-ref-area">
|
||||
<div class="commit-ref-strip" aria-label="Commit references">
|
||||
{#if refSummary.primaryBranch}
|
||||
<span
|
||||
class="compact-ref-chip branch"
|
||||
class:current={refSummary.primaryBranch.current}
|
||||
class:remote={refSummary.primaryBranch.kind === "remote"}
|
||||
title={branchDecorationTitle(refSummary.primaryBranch)}
|
||||
>
|
||||
<GitBranch class="compact-ref-branch-icon" size={10} aria-hidden="true" />
|
||||
<span>{refSummary.primaryBranch.label}</span>
|
||||
{#if branchStatusLabel(refSummary.primaryBranch)}
|
||||
<small class:up-to-date={Boolean(refSummary.primaryBranch.trackedRemote)}>
|
||||
{#if refSummary.primaryBranch.trackedRemote}<Check size={9} aria-hidden="true" />{/if}
|
||||
{branchStatusLabel(refSummary.primaryBranch).replace(/^✓\s*/, "")}
|
||||
</small>
|
||||
<span class="branch-ref-cluster" class:local-only={refSummary.primaryBranch.localOnly}>
|
||||
<span
|
||||
class="compact-ref-chip branch"
|
||||
class:current={refSummary.primaryBranch.current}
|
||||
class:remote={refSummary.primaryBranch.kind === "remote"}
|
||||
title={branchDecorationTitle(refSummary.primaryBranch)}
|
||||
>
|
||||
<GitBranch class="compact-ref-branch-icon" size={10} aria-hidden="true" />
|
||||
<span>{refSummary.primaryBranch.label}</span>
|
||||
{#if branchStatusLabel(refSummary.primaryBranch)}
|
||||
<small class:up-to-date={Boolean(refSummary.primaryBranch.trackedRemote)}>
|
||||
{#if refSummary.primaryBranch.trackedRemote}<Check size={9} aria-hidden="true" />{/if}
|
||||
{branchStatusLabel(refSummary.primaryBranch).replace(/^✓\s*/, "")}
|
||||
</small>
|
||||
{/if}
|
||||
</span>
|
||||
{#if refSummary.primaryBranch.localOnly}
|
||||
<span class="compact-ref-local-marker" title="This branch exists only locally and has not been published yet">
|
||||
<CloudOff size={9} aria-hidden="true" />LOCAL
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -899,6 +911,7 @@
|
||||
<i aria-hidden="true"></i>{branch.label}
|
||||
{#if branch.current}<small>Current</small>{/if}
|
||||
{#if branch.trackedRemote}<small>{branch.trackedRemote}</small>{/if}
|
||||
{#if branch.localOnly}<small class="local-only"><CloudOff size={9} aria-hidden="true" />Local only</small>{/if}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -129,6 +129,7 @@ export interface GitBranch {
|
||||
name: string;
|
||||
current: boolean;
|
||||
remote: boolean;
|
||||
upstream: string | null;
|
||||
}
|
||||
|
||||
export interface GitWorktree {
|
||||
|
||||
Reference in New Issue
Block a user