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
1871 lines
110 KiB
Svelte
1871 lines
110 KiB
Svelte
<script lang="ts">
|
||
import { onMount, tick } from "svelte";
|
||
import {
|
||
AlertTriangle,
|
||
BookOpen,
|
||
Check,
|
||
ChevronRight,
|
||
CircleHelp,
|
||
Clipboard,
|
||
Cloud,
|
||
Command,
|
||
GitBranch,
|
||
GitCommitHorizontal,
|
||
Home,
|
||
Keyboard,
|
||
Library,
|
||
Lightbulb,
|
||
ListChecks,
|
||
Search,
|
||
Sparkles,
|
||
Wrench,
|
||
X,
|
||
} from "@lucide/svelte";
|
||
|
||
interface CommandExample {
|
||
command: string;
|
||
description: string;
|
||
}
|
||
|
||
interface HelpSection {
|
||
id: string;
|
||
title: string;
|
||
summary: string;
|
||
steps?: string[];
|
||
commands?: CommandExample[];
|
||
note?: string;
|
||
}
|
||
|
||
interface HelpCategory {
|
||
id: string;
|
||
label: string;
|
||
description: string;
|
||
sections: HelpSection[];
|
||
}
|
||
|
||
interface Props {
|
||
language: "en" | "de";
|
||
onClose: () => void;
|
||
}
|
||
|
||
const deCategories: HelpCategory[] = [
|
||
{
|
||
id: "start",
|
||
label: "Erste Schritte",
|
||
description: "Repository öffnen, Oberfläche verstehen und den ersten Commit erstellen.",
|
||
sections: [
|
||
{
|
||
id: "start-workflow",
|
||
title: "Dein erster Gitty-Workflow",
|
||
summary: "Vom Repository bis zum veröffentlichten Commit in fünf klaren Schritten.",
|
||
steps: [
|
||
"Öffne ein vorhandenes Repository oder klone ein Projekt über die Repository-Verwaltung.",
|
||
"Bearbeite deine Dateien. Gitty zeigt Änderungen im Arbeitsverzeichnis automatisch an.",
|
||
"Prüfe den Diff und stage einzelne Dateien, Zeilen oder alle passenden Änderungen.",
|
||
"Schreibe eine aussagekräftige Commit-Nachricht und erstelle den Commit.",
|
||
"Nutze Fetch, Pull und Push, um deinen Stand mit dem Remote-Repository abzugleichen.",
|
||
],
|
||
},
|
||
{
|
||
id: "start-layout",
|
||
title: "Die Oberfläche im Überblick",
|
||
summary: "Links findest du Branches, Stashes und Dateien. In der Mitte prüfst und commitest du Änderungen; rechts siehst du Verlauf und Dateihistorie.",
|
||
note: "Fast alle Bereiche lassen sich über die Trennlinien in der Größe anpassen oder über ihren Kopf einklappen.",
|
||
},
|
||
{
|
||
id: "start-safety",
|
||
title: "Sicher arbeiten",
|
||
summary: "Prüfe vor Commit, Pull, Rebase oder Verwerfen immer den aktuellen Branch und die betroffenen Dateien. Gitty fragt bei destruktiven Aktionen nochmals nach.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "app",
|
||
label: "Arbeiten mit Gitty",
|
||
description: "Die wichtigsten Funktionen der App Schritt für Schritt.",
|
||
sections: [
|
||
{
|
||
id: "app-changes",
|
||
title: "Änderungen prüfen und stagen",
|
||
summary: "Wähle eine geänderte Datei, lies den Diff und verschiebe gezielt Änderungen in den Staging-Bereich.",
|
||
steps: [
|
||
"Unstaged enthält noch nicht vorbereitete Änderungen; Staged enthält den nächsten Commit-Inhalt.",
|
||
"Öffne eine Datei, um hinzugefügte und entfernte Zeilen zu prüfen.",
|
||
"Stage ganze Dateien oder nutze die Zeilen-/Hunk-Aktionen für kleinere, saubere Commits.",
|
||
"Verwerfen entfernt lokale Änderungen. Diese Aktion lässt sich nicht immer rückgängig machen.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-history",
|
||
title: "Verlauf, Suche und Vergleich",
|
||
summary: "Der Commit-Verlauf zeigt Branches und Merges. Mit der globalen Suche findest du die Einführung von Code oder den Verlauf einer Datei.",
|
||
steps: [
|
||
"Klappe einen Commit auf, um seine Dateien zu sehen.",
|
||
"Vergleiche zwei Commits über Compare in der Titelleiste.",
|
||
"Nutze Reflog, um auch verschobene oder nicht mehr sichtbare Referenzen wiederzufinden.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-stash",
|
||
title: "Zwischenstände mit Stash sichern",
|
||
summary: "Ein Stash parkt unvollständige Änderungen, ohne einen Commit zu erzeugen. Apply behält den Stash, Pop wendet ihn an und entfernt ihn.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "basics",
|
||
label: "Git-Grundlagen",
|
||
description: "Alltägliche Befehle für Status, Änderungen, Commits und Wiederherstellung.",
|
||
sections: [
|
||
{
|
||
id: "basics-inspect",
|
||
title: "Repository prüfen",
|
||
summary: "Diese Befehle verändern nichts und eignen sich immer als erster Blick auf den aktuellen Zustand.",
|
||
commands: [
|
||
{ command: "git status", description: "Arbeitsverzeichnis und Staging-Bereich anzeigen" },
|
||
{ command: "git diff", description: "Noch nicht gestagte Änderungen anzeigen" },
|
||
{ command: "git diff --staged", description: "Inhalt des nächsten Commits anzeigen" },
|
||
{ command: "git log --oneline --graph --decorate --all", description: "Kompakten Branch- und Commit-Verlauf anzeigen" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-commit",
|
||
title: "Änderungen speichern",
|
||
summary: "Stage nur zusammengehörige Änderungen und beschreibe im Commit, warum die Änderung nötig ist.",
|
||
commands: [
|
||
{ command: "git add <datei>", description: "Eine Datei für den Commit vormerken" },
|
||
{ command: "git add -p", description: "Änderungen interaktiv und abschnittsweise stagen" },
|
||
{ command: "git commit -m \"Kurze Beschreibung\"", description: "Einen Commit erstellen" },
|
||
{ command: "git commit --amend", description: "Den letzten lokalen Commit ergänzen oder umbenennen" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-undo",
|
||
title: "Änderungen rückgängig machen",
|
||
summary: "Restore arbeitet am Arbeitsverzeichnis oder Staging-Bereich; Revert erzeugt einen neuen Gegen-Commit und ist für veröffentlichte Historie sicherer.",
|
||
commands: [
|
||
{ command: "git restore <datei>", description: "Lokale, noch nicht gestagte Änderung verwerfen" },
|
||
{ command: "git restore --staged <datei>", description: "Datei aus dem Staging-Bereich entfernen" },
|
||
{ command: "git revert <commit>", description: "Wirkung eines Commits durch neuen Commit umkehren" },
|
||
],
|
||
note: "Vorsicht: git reset --hard verwirft lokale Änderungen. Verwende den Befehl nur, wenn du den Verlust bewusst akzeptierst.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "branches",
|
||
label: "Branches & Merges",
|
||
description: "Parallele Arbeit organisieren, zusammenführen und aufräumen.",
|
||
sections: [
|
||
{
|
||
id: "branches-manage",
|
||
title: "Branches verwalten",
|
||
summary: "Ein Branch ist ein beweglicher Zeiger auf eine Commit-Linie. Erstelle für jede getrennte Aufgabe einen eigenen Branch.",
|
||
commands: [
|
||
{ command: "git switch -c feature/meine-aenderung", description: "Neuen Branch erstellen und wechseln" },
|
||
{ command: "git switch main", description: "Zu einem vorhandenen Branch wechseln" },
|
||
{ command: "git branch -vv", description: "Lokale Branches mit Upstream-Status anzeigen" },
|
||
{ command: "git branch -d <branch>", description: "Bereits zusammengeführten Branch löschen" },
|
||
],
|
||
},
|
||
{
|
||
id: "branches-integrate",
|
||
title: "Merge oder Rebase?",
|
||
summary: "Merge bewahrt die tatsächliche Verzweigung. Rebase setzt lokale Commits auf eine neue Basis und erzeugt eine lineare Historie.",
|
||
commands: [
|
||
{ command: "git merge <branch>", description: "Einen Branch in den aktuellen Branch integrieren" },
|
||
{ command: "git rebase main", description: "Lokale Commits auf den aktuellen Stand von main setzen" },
|
||
{ command: "git rebase --continue", description: "Rebase nach gelöstem Konflikt fortsetzen" },
|
||
{ command: "git rebase --abort", description: "Rebase abbrechen und Ausgangszustand wiederherstellen" },
|
||
],
|
||
note: "Rebase keine Commits, an denen andere bereits weiterarbeiten. Das Umschreiben veröffentlichter Historie verursacht unnötige Konflikte.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "remote",
|
||
label: "Remote & Sync",
|
||
description: "Änderungen sicher mit GitHub, GitLab oder anderen Remotes austauschen.",
|
||
sections: [
|
||
{
|
||
id: "remote-sync",
|
||
title: "Fetch, Pull und Push",
|
||
summary: "Fetch lädt Referenzen ohne deine Dateien zu ändern. Pull integriert Remote-Änderungen. Push veröffentlicht deine Commits.",
|
||
commands: [
|
||
{ command: "git fetch --all --prune", description: "Remote-Stände laden und entfernte Referenzen aufräumen" },
|
||
{ command: "git pull --rebase", description: "Remote-Änderungen laden und lokale Commits darauf neu abspielen" },
|
||
{ command: "git push -u origin <branch>", description: "Branch erstmals veröffentlichen und Upstream setzen" },
|
||
{ command: "git remote -v", description: "Konfigurierte Remote-Adressen anzeigen" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-ahead",
|
||
title: "Ahead und Behind verstehen",
|
||
summary: "Ahead bedeutet: lokale Commits wurden noch nicht gepusht. Behind bedeutet: im Remote liegen neue Commits, die lokal fehlen. Beides gleichzeitig weist auf auseinanderlaufende Historien hin.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "troubleshooting",
|
||
label: "Probleme lösen",
|
||
description: "Konflikte, verlorene Commits und typische Fehlermeldungen verstehen.",
|
||
sections: [
|
||
{
|
||
id: "trouble-conflicts",
|
||
title: "Merge-Konflikte lösen",
|
||
summary: "Ein Konflikt entsteht, wenn Git Änderungen nicht eindeutig kombinieren kann. Gitty bietet dafür einen eigenen Resolve-Dialog.",
|
||
steps: [
|
||
"Öffne jede Konfliktdatei und vergleiche Current, Incoming und das kombinierte Ergebnis.",
|
||
"Übernimm eine Seite oder bearbeite den Zielinhalt manuell.",
|
||
"Markiere die Datei als gelöst und prüfe anschließend den vollständigen Diff.",
|
||
"Führe Merge, Rebase oder Cherry-pick fort – oder brich die Operation vollständig ab.",
|
||
],
|
||
commands: [
|
||
{ command: "git status", description: "Konfliktdateien und laufende Operation anzeigen" },
|
||
{ command: "git merge --abort", description: "Laufenden Merge abbrechen" },
|
||
{ command: "git cherry-pick --abort", description: "Laufenden Cherry-pick abbrechen" },
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-reflog",
|
||
title: "Verlorene Commits wiederfinden",
|
||
summary: "Reflog protokolliert lokale Bewegungen von HEAD und Branches. Kopiere den Hash des gesuchten Eintrags und erstelle daraus einen Sicherungs-Branch.",
|
||
commands: [
|
||
{ command: "git reflog", description: "Lokale Referenzbewegungen anzeigen" },
|
||
{ command: "git branch recovery/<name> <commit>", description: "Gefundenen Commit dauerhaft über Branch sichern" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "shortcuts",
|
||
label: "Tastenkürzel",
|
||
description: "Gitty schneller und ohne Maus bedienen.",
|
||
sections: [
|
||
{
|
||
id: "shortcuts-main",
|
||
title: "Globale Bedienung",
|
||
summary: "Die Hilfe ist überall erreichbar. Dialoge lassen sich konsistent schließen und Suchfelder direkt fokussieren.",
|
||
commands: [
|
||
{ command: "Ctrl + /", description: "Diese Hilfe öffnen" },
|
||
{ command: "Escape", description: "Aktuelles Overlay oder Dialogfenster schließen" },
|
||
{ command: "Tab / Shift + Tab", description: "Zwischen Bedienelementen wechseln" },
|
||
{ command: "Enter / Leertaste", description: "Fokussierte Aktion ausführen" },
|
||
],
|
||
note: "Auf macOS kannst du für Ctrl in der Regel die Command-Taste verwenden.",
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
const enCategories: HelpCategory[] = [
|
||
{
|
||
id: "start",
|
||
label: "Getting started",
|
||
description: "Open a repository, understand the interface, and create your first commit.",
|
||
sections: [
|
||
{
|
||
id: "start-workflow",
|
||
title: "Your first Gitty workflow",
|
||
summary: "From repository to published commit in five clear steps.",
|
||
steps: [
|
||
"Open an existing repository or clone a project from Repository Management.",
|
||
"Edit your files. Gitty automatically displays working-tree changes.",
|
||
"Review the diff and stage individual files, lines, or all related changes.",
|
||
"Write a meaningful commit message and create the commit.",
|
||
"Use Fetch, Pull, and Push to synchronize with the remote repository.",
|
||
],
|
||
},
|
||
{
|
||
id: "start-layout",
|
||
title: "Interface overview",
|
||
summary: "Branches, stashes, and files are on the left. Review and commit changes in the center; history and file history are on the right.",
|
||
note: "Most areas can be resized using their dividers or collapsed from their header.",
|
||
},
|
||
{
|
||
id: "start-safety",
|
||
title: "Work safely",
|
||
summary: "Before committing, pulling, rebasing, or discarding, always check the current branch and affected files. Gitty confirms destructive actions.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "app",
|
||
label: "Working with Gitty",
|
||
description: "The app's most important features, explained step by step.",
|
||
sections: [
|
||
{
|
||
id: "app-changes",
|
||
title: "Review and stage changes",
|
||
summary: "Select a changed file, review its diff, and move changes into the staging area.",
|
||
steps: [
|
||
"Unstaged contains changes not yet prepared; Staged contains the next commit.",
|
||
"Open a file to review added and removed lines.",
|
||
"Stage whole files or use line and hunk actions for focused commits.",
|
||
"Discard removes local changes and cannot always be undone.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-history",
|
||
title: "History, search, and comparison",
|
||
summary: "Commit History shows branches and merges. Global Search finds where code was introduced or displays a file's history.",
|
||
steps: [
|
||
"Expand a commit to inspect its files.",
|
||
"Compare two commits with Compare in the title bar.",
|
||
"Use Reflog to recover moved or otherwise hidden references.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-stash",
|
||
title: "Save temporary work with Stash",
|
||
summary: "A stash parks unfinished changes without creating a commit. Apply keeps the stash; Pop applies and removes it.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "basics",
|
||
label: "Git basics",
|
||
description: "Everyday commands for status, changes, commits, and recovery.",
|
||
sections: [
|
||
{
|
||
id: "basics-inspect",
|
||
title: "Inspect a repository",
|
||
summary: "These commands do not change anything and are always a good first look at the current state.",
|
||
commands: [
|
||
{ command: "git status", description: "Show the working tree and staging area" },
|
||
{ command: "git diff", description: "Show changes that have not been staged" },
|
||
{ command: "git diff --staged", description: "Show the contents of the next commit" },
|
||
{ command: "git log --oneline --graph --decorate --all", description: "Show a compact branch and commit graph" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-commit",
|
||
title: "Save changes",
|
||
summary: "Stage only related changes and explain why the change is needed in the commit message.",
|
||
commands: [
|
||
{ command: "git add <file>", description: "Stage one file" },
|
||
{ command: "git add -p", description: "Stage changes interactively by hunk" },
|
||
{ command: "git commit -m \"Short description\"", description: "Create a commit" },
|
||
{ command: "git commit --amend", description: "Update or rename the latest local commit" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-undo",
|
||
title: "Undo changes",
|
||
summary: "Restore changes the working tree or staging area. Revert creates a new inverse commit and is safer for published history.",
|
||
commands: [
|
||
{ command: "git restore <file>", description: "Discard an unstaged local change" },
|
||
{ command: "git restore --staged <file>", description: "Remove a file from the staging area" },
|
||
{ command: "git revert <commit>", description: "Undo a commit through a new commit" },
|
||
],
|
||
note: "Caution: git reset --hard discards local changes. Use it only when that data loss is intentional.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "branches",
|
||
label: "Branches & merges",
|
||
description: "Organize parallel work, integrate it, and clean up afterward.",
|
||
sections: [
|
||
{
|
||
id: "branches-manage",
|
||
title: "Manage branches",
|
||
summary: "A branch is a movable pointer to a commit line. Create a separate branch for each independent task.",
|
||
commands: [
|
||
{ command: "git switch -c feature/my-change", description: "Create and switch to a new branch" },
|
||
{ command: "git switch main", description: "Switch to an existing branch" },
|
||
{ command: "git branch -vv", description: "Show local branches and upstream status" },
|
||
{ command: "git branch -d <branch>", description: "Delete a branch that has already been merged" },
|
||
],
|
||
},
|
||
{
|
||
id: "branches-integrate",
|
||
title: "Merge or rebase?",
|
||
summary: "Merge preserves the actual branch structure. Rebase moves local commits onto a new base for a linear history.",
|
||
commands: [
|
||
{ command: "git merge <branch>", description: "Integrate a branch into the current branch" },
|
||
{ command: "git rebase main", description: "Move local commits onto the latest main" },
|
||
{ command: "git rebase --continue", description: "Continue after resolving a conflict" },
|
||
{ command: "git rebase --abort", description: "Abort and restore the original state" },
|
||
],
|
||
note: "Do not rebase commits other people are already using. Rewriting published history creates avoidable conflicts.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "remote",
|
||
label: "Remote & sync",
|
||
description: "Exchange changes safely with GitHub, GitLab, or other remotes.",
|
||
sections: [
|
||
{
|
||
id: "remote-sync",
|
||
title: "Fetch, pull, and push",
|
||
summary: "Fetch downloads references without changing files. Pull integrates remote changes. Push publishes your commits.",
|
||
commands: [
|
||
{ command: "git fetch --all --prune", description: "Download remote state and remove stale references" },
|
||
{ command: "git pull --rebase", description: "Download changes and replay local commits on top" },
|
||
{ command: "git push -u origin <branch>", description: "Publish a branch and set its upstream" },
|
||
{ command: "git remote -v", description: "Show configured remote URLs" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-ahead",
|
||
title: "Understanding Ahead and Behind",
|
||
summary: "Ahead means local commits have not been pushed. Behind means remote commits are missing locally. Both at once means the histories have diverged.",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "troubleshooting",
|
||
label: "Troubleshooting",
|
||
description: "Resolve conflicts, recover lost commits, and understand common errors.",
|
||
sections: [
|
||
{
|
||
id: "trouble-conflicts",
|
||
title: "Resolve merge conflicts",
|
||
summary: "A conflict occurs when Git cannot combine changes unambiguously. Gitty provides a dedicated Resolve dialog.",
|
||
steps: [
|
||
"Open each conflicted file and compare Current, Incoming, and the combined result.",
|
||
"Accept one side or edit the final content manually.",
|
||
"Mark the file resolved and review the complete diff.",
|
||
"Continue the merge, rebase, or cherry-pick—or abort the operation.",
|
||
],
|
||
commands: [
|
||
{ command: "git status", description: "Show conflicts and the active operation" },
|
||
{ command: "git merge --abort", description: "Abort the current merge" },
|
||
{ command: "git cherry-pick --abort", description: "Abort the current cherry-pick" },
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-reflog",
|
||
title: "Recover lost commits",
|
||
summary: "Reflog records local movements of HEAD and branches. Copy the desired hash and create a recovery branch from it.",
|
||
commands: [
|
||
{ command: "git reflog", description: "Show local reference movements" },
|
||
{ command: "git branch recovery/<name> <commit>", description: "Preserve the recovered commit with a branch" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "shortcuts",
|
||
label: "Keyboard shortcuts",
|
||
description: "Use Gitty quickly without reaching for the mouse.",
|
||
sections: [
|
||
{
|
||
id: "shortcuts-main",
|
||
title: "Global controls",
|
||
summary: "Help is available everywhere. Dialogs close consistently and search fields receive focus automatically.",
|
||
commands: [
|
||
{ command: "Ctrl + /", description: "Open this help center" },
|
||
{ command: "Escape", description: "Close the current overlay or dialog" },
|
||
{ command: "Tab / Shift + Tab", description: "Move between controls" },
|
||
{ command: "Enter / Space", description: "Activate the focused control" },
|
||
],
|
||
note: "On macOS, you can generally use Command instead of Ctrl.",
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
// Extended handbook chapters. Keeping these additions next to the shared data makes
|
||
// it straightforward to compare the German and English coverage section by section.
|
||
deCategories.find((category) => category.id === "start")?.sections.push(
|
||
{
|
||
id: "start-model",
|
||
title: "Das Git-Grundmodell verstehen",
|
||
summary: "Git speichert keine fortlaufende Liste einzelner Dateiänderungen, sondern verknüpfte Schnappschüsse deines Projekts. HEAD zeigt auf deinen aktuellen Commit; der Branch-Name bewegt sich beim Commit mit.",
|
||
steps: [
|
||
"Arbeitsverzeichnis: Hier bearbeitest du echte Dateien. Änderungen sind noch nicht Teil eines Commits.",
|
||
"Staging-Bereich: Hier stellst du exakt den Inhalt des nächsten Commits zusammen.",
|
||
"Lokales Repository: Commits, Branches und Tags liegen zunächst nur auf deinem Rechner.",
|
||
"Remote-Repository: Push veröffentlicht lokale Commits; Fetch lädt fremde Referenzen; Pull lädt und integriert.",
|
||
],
|
||
note: "Der Staging-Bereich ist kein zusätzlicher Ordner. Er ist ein Git-Schnappschuss, den Gitty als „Staged“ darstellt.",
|
||
},
|
||
{
|
||
id: "start-before-work",
|
||
title: "Checkliste vor jeder Aufgabe",
|
||
summary: "Ein kurzer Zustandscheck verhindert die meisten versehentlichen Commits und komplizierten Konflikte.",
|
||
commands: [
|
||
{ command: "git status --short --branch", description: "Branch, Upstream und Änderungen kompakt prüfen" },
|
||
{ command: "git fetch --prune", description: "Remote-Stand aktualisieren, ohne Dateien zu verändern" },
|
||
{ command: "git log --oneline --decorate -10", description: "Die letzten zehn Commits und Referenzen prüfen" },
|
||
],
|
||
steps: [
|
||
"Prüfe, ob du auf dem richtigen Branch bist.",
|
||
"Sichere oder stash unvollständige Änderungen, bevor du den Branch wechselst.",
|
||
"Hole Remote-Informationen mit Fetch und entscheide erst danach über Pull, Rebase oder Merge.",
|
||
],
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "app")?.sections.push(
|
||
{
|
||
id: "app-repositories",
|
||
title: "Repositories und Tabs verwalten",
|
||
summary: "Die Repository-Verwaltung bündelt offene, zuletzt verwendete und favorisierte Projekte. Jedes offene Repository erhält einen eigenen Tab mit Branch- und Änderungsstatus.",
|
||
steps: [
|
||
"Browse öffnet ein bestehendes lokales Repository; Clone lädt ein Remote-Repository in einen neuen Ordner.",
|
||
"Markiere häufig verwendete Projekte als Favorit, damit sie unabhängig von der Verlaufsliste sichtbar bleiben.",
|
||
"Wechsle über die Tabs zwischen Projekten. Gitty merkt sich Status, Größen und ausgewählte Bereiche pro Sitzung.",
|
||
"Öffne das Tab-Kontextmenü, um ein Repository aus der aktuellen Arbeitsfläche zu entfernen, ohne Dateien zu löschen.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-commit-detail",
|
||
title: "Saubere Commits in Gitty erstellen",
|
||
summary: "Ein guter Commit enthält genau eine logisch zusammengehörige Änderung und lässt sich unabhängig erklären, prüfen und notfalls zurücknehmen.",
|
||
steps: [
|
||
"Prüfe zuerst Unstaged und den vollständigen Diff jeder betroffenen Datei.",
|
||
"Stage nur passende Dateien, Hunks oder Zeilen. Tests und Implementierung dürfen zusammengehören; zufällige Formatierungen meist nicht.",
|
||
"Lies anschließend ausschließlich den Staged-Diff – genau dieser Inhalt wird committed.",
|
||
"Formuliere eine kurze, imperative Betreffzeile, zum Beispiel „Handle expired credentials“.",
|
||
"Nutze Amend nur, solange der letzte Commit noch nicht von anderen verwendet wird.",
|
||
],
|
||
note: "Wenn du im Staged-Diff etwas Überraschendes siehst, entferne es wieder aus dem Staging-Bereich. Ein Commit ist der falsche Ort für „wird schon passen“.",
|
||
},
|
||
{
|
||
id: "app-branches-tags",
|
||
title: "Branches und Tags in der App",
|
||
summary: "Das Branch-Panel zeigt lokale und Remote-Branches sowie Ahead/Behind. Über das Kontextmenü kannst du wechseln, erstellen, umbenennen, löschen, mergen oder rebasen.",
|
||
steps: [
|
||
"Erstelle einen Branch vom aktuellen HEAD oder gezielt von einem Commit im Verlauf.",
|
||
"Ein Checkout/Switch aktualisiert Arbeitsverzeichnis und HEAD. Sichere inkompatible lokale Änderungen vorher.",
|
||
"Tags markieren feste Commits, typischerweise Releases. Ein Tag bewegt sich nicht automatisch weiter.",
|
||
"Remote-Branches sind zunächst Referenzen. Erstelle beim Wechsel einen lokalen Tracking-Branch.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-history-tools",
|
||
title: "History, Dateiverlauf, Blame und Restore",
|
||
summary: "Gitty verbindet Commit-Graph, Dateiverlauf und Wiederherstellung, damit du Ursache und Entwicklung einer Änderung nachvollziehen kannst.",
|
||
steps: [
|
||
"Wähle eine Datei im Explorer, um ihren eigenen Verlauf unabhängig vom Gesamtprojekt zu sehen.",
|
||
"Blame ordnet jeder aktuellen Zeile den letzten verändernden Commit zu. Nutze es als Einstieg, nicht als Schuldzuweisung.",
|
||
"Compare zeigt Unterschiede zwischen zwei beliebigen Commits oder Branch-Spitzen.",
|
||
"Restore from commit übernimmt eine ältere Dateiversion ins Arbeitsverzeichnis. Prüfe und committe das Ergebnis anschließend normal.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-search",
|
||
title: "Code-Ursprung mit Global Search finden",
|
||
summary: "Die Code-Suche untersucht die Commit-Historie und findet, in welchem Commit eine Zeichenfolge oder Funktion eingeführt wurde. Die Dateisuche verbindet Pfadsuche mit Dateihistorie.",
|
||
steps: [
|
||
"Suche nach einem stabilen, möglichst eindeutigen Ausschnitt statt nach einer häufigen Einzelzeile.",
|
||
"Aktiviere Groß-/Kleinschreibung nur, wenn sie die Treffermenge sinnvoll reduziert.",
|
||
"Öffne einen Treffer als Diff, um die Einführung im Kontext des gesamten Commits zu prüfen.",
|
||
"Bei Umbenennungen zeigt Gitty alten und neuen Pfad, soweit Git sie aus der Ähnlichkeit ableiten kann.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-operations",
|
||
title: "Laufende Git-Operationen sicher beenden",
|
||
summary: "Während Rebase oder Cherry-pick zeigt Gitty einen speziellen Status. Löse alle Konflikte und entscheide dann bewusst zwischen Continue und Abort.",
|
||
steps: [
|
||
"Resolve öffnet jede Konfliktdatei mit Current, Incoming und editierbarem Zielinhalt.",
|
||
"Markiere erst nach inhaltlicher Prüfung als gelöst; „keine Konfliktmarker mehr“ bedeutet nicht automatisch „fachlich richtig“.",
|
||
"Continue verarbeitet den nächsten Commit und kann weitere Konflikte erzeugen.",
|
||
"Abort stellt den Zustand vor Beginn der gesamten Operation wieder her.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-credentials-settings",
|
||
title: "Zugangsdaten, AI und Einstellungen",
|
||
summary: "Gitty fragt Zugangsdaten erst bei einer authentifizierten Remote-Aktion ab. App-Theme, Sprache und anonyme Analytics liegen in den Einstellungen; AI-Anbieter werden separat konfiguriert.",
|
||
steps: [
|
||
"Verwende für HTTPS-Remotes ein persönliches Zugriffstoken statt des Account-Passworts.",
|
||
"Begrenze Token-Rechte und Laufzeit auf das tatsächlich benötigte Minimum.",
|
||
"AI-generierte Commit-Texte sind Vorschläge: Prüfe Inhalt, sensible Daten und tatsächlichen Staged-Diff.",
|
||
"Gitty sendet über Analytics keine Pfade, Remotes, Branches, Commit-Texte, Dateinamen, Diffs, Zugangsdaten oder Code.",
|
||
],
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "basics")?.sections.push(
|
||
{
|
||
id: "basics-config",
|
||
title: "Identität und Konfiguration",
|
||
summary: "Git schreibt Name und E-Mail in jeden Commit. Globale Werte gelten für alle Repositories; lokale Werte überschreiben sie nur im aktuellen Projekt.",
|
||
commands: [
|
||
{ command: "git config --global user.name \"Ada Lovelace\"", description: "Globalen Anzeigenamen setzen" },
|
||
{ command: "git config --global user.email \"ada@example.com\"", description: "Globale Commit-E-Mail setzen" },
|
||
{ command: "git config --list --show-origin", description: "Wirksame Einstellungen und Quelldateien anzeigen" },
|
||
{ command: "git config user.email \"work@example.com\"", description: "E-Mail nur für das aktuelle Repository setzen" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-ignore",
|
||
title: ".gitignore richtig verwenden",
|
||
summary: ".gitignore verhindert, dass neue, noch ungetrackte Dateien vorgeschlagen werden. Bereits getrackte Dateien werden dadurch nicht automatisch entfernt.",
|
||
commands: [
|
||
{ command: "git check-ignore -v <datei>", description: "Zeigen, welche Ignore-Regel auf eine Datei wirkt" },
|
||
{ command: "git rm --cached <datei>", description: "Datei nur aus Git entfernen, lokal aber behalten" },
|
||
{ command: "git status --ignored", description: "Auch ignorierte Dateien anzeigen" },
|
||
],
|
||
note: "Committe niemals Secrets. .gitignore verhindert zukünftiges Tracking, entfernt aber keine Geheimnisse aus bereits vorhandenen Commits.",
|
||
},
|
||
{
|
||
id: "basics-show",
|
||
title: "Commits und Objekte untersuchen",
|
||
summary: "Hashes identifizieren Git-Objekte. Meist reichen die ersten eindeutigen Zeichen; Referenzen wie HEAD~1 oder main sind lesbare Zeiger auf Commits.",
|
||
commands: [
|
||
{ command: "git show <commit>", description: "Metadaten und Patch eines Commits anzeigen" },
|
||
{ command: "git show <commit>:<pfad>", description: "Dateiinhalt aus einem bestimmten Commit ausgeben" },
|
||
{ command: "git diff <von>..<bis>", description: "Zwei Zustände direkt vergleichen" },
|
||
{ command: "git log --follow -- <datei>", description: "Dateiverlauf über Umbenennungen hinweg verfolgen" },
|
||
],
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "app")?.sections.push(
|
||
{
|
||
id: "app-line-staging",
|
||
title: "Änderungen zeilenweise stagen",
|
||
summary: "Im Patch-Dialog kannst du nicht nur vollständige Dateien oder Hunks, sondern einzelne hinzugefügte und entfernte Zeilen für den nächsten Commit auswählen.",
|
||
steps: [
|
||
"Öffne im Bereich Unstaged den Patch einer geänderten Textdatei.",
|
||
"Klicke auf die Auswahlfläche vor einer grünen oder roten Zeile. Mit Shift-Klick markierst du einen zusammenhängenden Bereich.",
|
||
"Die Auswahlleiste zeigt die Anzahl der gewählten Zeilen. Stage selected übernimmt nur diese Auswahl in den Staging-Bereich.",
|
||
"Öffne denselben Dialog unter Staged und nutze Unstage selected, wenn einzelne Zeilen wieder zurück ins Arbeitsverzeichnis sollen.",
|
||
"Prüfe danach den Staged-Diff. Er ist die verbindliche Vorschau des nächsten Commits.",
|
||
],
|
||
note: "Bei einer geänderten Zeile stellt Git intern meist eine entfernte und eine hinzugefügte Zeile dar. Wähle beide Seiten, wenn die vollständige Ersetzung in denselben Commit gehört.",
|
||
},
|
||
{
|
||
id: "app-line-discard",
|
||
title: "Hunks und einzelne Zeilen verwerfen",
|
||
summary: "Discard selected verwirft nur die markierten Zeilen; Discard Hunk verwirft den vollständigen Abschnitt. Gitty zeigt vor dem Löschen eine Sicherheitsabfrage.",
|
||
steps: [
|
||
"Kontrolliere, ob der Dialog Staged changes oder Unstaged changes anzeigt.",
|
||
"Markiere exakt die Zeilen, die nicht behalten werden sollen.",
|
||
"Lies in der Sicherheitsabfrage Pfad und Umfang und bestätige erst danach.",
|
||
"Aktualisiere den Patch, falls Dateien während des geöffneten Dialogs extern verändert wurden.",
|
||
],
|
||
note: "Vorsicht: Verwerfen kann nicht zuverlässig rückgängig gemacht werden. Erstelle bei wichtigen Zwischenständen zuerst einen Commit oder Stash.",
|
||
},
|
||
{
|
||
id: "app-package-updates",
|
||
title: "Gitty unter Arch Linux installieren und aktualisieren",
|
||
summary: "Das AUR-Paket gitty-desktop lädt das öffentliche Gitea-Release und baut Gitty lokal aus dem Quellcode.",
|
||
commands: [
|
||
{ command: "yay -S gitty-desktop", description: "Gitty mit yay aus dem AUR bauen, installieren oder aktualisieren" },
|
||
{ command: "paru -S gitty-desktop", description: "Gitty alternativ mit paru bauen, installieren oder aktualisieren" },
|
||
{ command: "git clone https://aur.archlinux.org/gitty-desktop.git && cd gitty-desktop && makepkg -si", description: "AUR-Paket ohne AUR-Helfer prüfen und manuell bauen" },
|
||
{ command: "pacman -Qi gitty-desktop", description: "Installierte Version und Paketinformationen anzeigen" },
|
||
],
|
||
steps: [
|
||
"Das PKGBUILD lädt das öffentliche Quellarchiv des jeweiligen Gitea-Tags herunter.",
|
||
"Die Pipeline aktualisiert Version, Prüfsumme und .SRCINFO im AUR-Paket gitty-desktop.",
|
||
"AUR-Helfer erkennen neue Versionen und erzeugen daraus lokal ein normales Pacman-Paket.",
|
||
"Ohne AUR-Helfer kannst du das AUR-Git-Repository klonen, die Dateien prüfen und makepkg -si ausführen.",
|
||
],
|
||
note: "AUR-Pakete werden von Nutzern gepflegt. Prüfe PKGBUILD und .SRCINFO vor der Installation, besonders nach größeren Änderungen.",
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "branches")?.sections.push(
|
||
{
|
||
id: "branches-worktrees-concept",
|
||
title: "Was ein Worktree ist",
|
||
summary: "Ein Repository besitzt eine gemeinsame Git-Datenbank, kann aber mehrere Arbeitsordner haben. Jeder Worktree hat eigene ausgecheckte Dateien und normalerweise einen eigenen Branch; Commits und Referenzen sind sofort in allen Worktrees sichtbar.",
|
||
steps: [
|
||
"Das Haupt-Worktree ist der Ordner, den du ursprünglich als Repository geöffnet hast.",
|
||
"Ein zusätzlicher Worktree ist kein vollständiger Clone: Git-Objekte und Historie werden gemeinsam genutzt, nur Arbeitsordner und Index sind getrennt.",
|
||
"Änderungen in Dateien bleiben im jeweiligen Worktree. Ein dort erstellter Commit ist dagegen sofort Teil desselben Repositorys.",
|
||
"Worktrees eignen sich besonders für parallele Features, dringende Hotfixes, Reviews oder längere Builds auf einem zweiten Branch.",
|
||
"Für vollständig unabhängige Remotes, Konfigurationen oder Experimente ist ein eigener Clone weiterhin die passendere Wahl.",
|
||
],
|
||
note: "Ein Branch kann normalerweise nicht gleichzeitig in zwei Worktrees ausgecheckt werden. Das verhindert, dass zwei Ordner denselben Branch widersprüchlich verändern.",
|
||
},
|
||
{
|
||
id: "branches-worktrees",
|
||
title: "Mehrere Branches mit Worktrees parallel öffnen",
|
||
summary: "Ein Worktree checkt einen weiteren Branch in einen eigenen Ordner aus. So kannst du an mehreren Aufgaben arbeiten, ohne Branches im Hauptordner umzuschalten oder lokale Änderungen zu stashen.",
|
||
steps: [
|
||
"Öffne im Branch-Panel unter Tags den Reiter Worktrees.",
|
||
"Wähle Existing branch für einen vorhandenen lokalen Branch, New branch für einen neuen Branch oder Detached für einen festen Commit ohne Branch.",
|
||
"Lege einen leeren Zielordner fest und erstelle den Worktree. Open tab öffnet ihn anschließend als eigenen Repository-Tab.",
|
||
"Move verschiebt einen nicht aktiven Worktree. Lock schützt ihn vor versehentlichem Prune oder Entfernen, etwa auf einem externen Laufwerk.",
|
||
"Remove löscht den Worktree-Ordner und seine Registrierung, behält aber den Branch. Prune entfernt nur veraltete Registrierungen nicht mehr vorhandener Ordner.",
|
||
],
|
||
commands: [
|
||
{ command: "git worktree list", description: "Alle registrierten Worktrees anzeigen" },
|
||
{ command: "git worktree add ../projekt-fix fix/login", description: "Vorhandenen Branch in neuem Ordner auschecken" },
|
||
{ command: "git worktree add -b feature/name ../projekt-feature main", description: "Neuen Branch und Worktree von main erstellen" },
|
||
{ command: "git worktree prune --dry-run", description: "Veraltete Registrierungen vor dem Aufräumen prüfen" },
|
||
],
|
||
note: "Ein Branch kann normalerweise nur in einem Worktree gleichzeitig ausgecheckt sein. Ein Worktree mit lokalen Änderungen sollte vor Remove committed, gestasht oder bewusst erzwungen entfernt werden.",
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "branches")?.sections.push(
|
||
{
|
||
id: "branches-strategy",
|
||
title: "Eine einfache Branch-Strategie",
|
||
summary: "Kurze, fokussierte Branches reduzieren Konflikte. Aktualisiere sie regelmäßig und integriere sie nach Review möglichst schnell.",
|
||
steps: [
|
||
"Starte vom aktuellen main und gib dem Branch einen beschreibenden Namen wie feature/help-search.",
|
||
"Committe kleine, nachvollziehbare Einheiten und pushe den Branch als Sicherung und für Review.",
|
||
"Synchronisiere vor Abschluss mit dem aktuellen Ziel-Branch und löse Konflikte im eigenen Branch.",
|
||
"Merge nach bestandenem Review und lösche den kurzlebigen Branch lokal sowie remote.",
|
||
],
|
||
},
|
||
{
|
||
id: "branches-cherry-pick",
|
||
title: "Cherry-pick gezielt einsetzen",
|
||
summary: "Cherry-pick kopiert die Änderung eines vorhandenen Commits als neuen Commit auf den aktuellen Branch. Das ist praktisch für einzelne Fixes, ersetzt aber keine normale Branch-Integration.",
|
||
commands: [
|
||
{ command: "git cherry-pick <commit>", description: "Einen Commit auf den aktuellen Branch kopieren" },
|
||
{ command: "git cherry-pick --no-commit <commit>", description: "Änderung übernehmen, aber vor dem Commit weiter bearbeiten" },
|
||
{ command: "git cherry-pick --continue", description: "Nach Konfliktlösung fortsetzen" },
|
||
{ command: "git cherry-pick --abort", description: "Gesamten Cherry-pick abbrechen" },
|
||
],
|
||
},
|
||
{
|
||
id: "branches-interactive-rebase",
|
||
title: "Interactive Rebase",
|
||
summary: "Vor dem Veröffentlichen kannst du lokale Commits neu ordnen, umbenennen, zusammenfassen oder entfernen. Gitty bietet dafür einen visuellen Rebase-Plan.",
|
||
steps: [
|
||
"Pick behält einen Commit, Reword ändert seine Nachricht, Squash/Fixup kombiniert ihn mit dem vorherigen Commit, Drop entfernt ihn.",
|
||
"Ordne Abhängigkeiten so, dass jeder Zwischenschritt möglichst baubar und verständlich bleibt.",
|
||
"Prüfe nach dem Rebase Tests, Commit-Reihenfolge und finalen Diff gegen den Ziel-Branch.",
|
||
],
|
||
note: "Interactive Rebase erzeugt neue Commit-Hashes. Verwende ihn bevorzugt für deine eigenen, noch nicht gemeinsam genutzten Commits.",
|
||
},
|
||
{
|
||
id: "branches-tags",
|
||
title: "Releases mit Tags markieren",
|
||
summary: "Ein annotierter Tag speichert zusätzlich Autor, Datum und Nachricht und eignet sich deshalb besser für Releases als ein einfacher Lightweight-Tag.",
|
||
commands: [
|
||
{ command: "git tag -a v1.2.0 -m \"Release 1.2.0\"", description: "Annotierten Release-Tag erstellen" },
|
||
{ command: "git show v1.2.0", description: "Tag und zugehörigen Commit prüfen" },
|
||
{ command: "git push origin v1.2.0", description: "Einen bestimmten Tag veröffentlichen" },
|
||
{ command: "git push origin --tags", description: "Alle noch fehlenden lokalen Tags veröffentlichen" },
|
||
],
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "remote")?.sections.push(
|
||
{
|
||
id: "remote-tracking",
|
||
title: "Tracking-Branches und Upstream",
|
||
summary: "Der Upstream verbindet einen lokalen Branch mit seiner Remote-Referenz. Dadurch wissen Pull, Push und Ahead/Behind, welche beiden Linien verglichen werden.",
|
||
commands: [
|
||
{ command: "git branch --show-current", description: "Aktuellen lokalen Branch anzeigen" },
|
||
{ command: "git branch -u origin/<branch>", description: "Upstream für den aktuellen Branch setzen" },
|
||
{ command: "git branch -vv", description: "Upstream und Ahead/Behind aller lokalen Branches anzeigen" },
|
||
{ command: "git push -u origin HEAD", description: "Aktuellen Branch veröffentlichen und Upstream setzen" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-safe-pull",
|
||
title: "Sicher synchronisieren",
|
||
summary: "Fetch ist immer der kontrollierteste erste Schritt. Danach kannst du den Unterschied prüfen und bewusst Merge oder Rebase wählen.",
|
||
commands: [
|
||
{ command: "git fetch origin", description: "Remote-Informationen laden, ohne den lokalen Branch zu ändern" },
|
||
{ command: "git log --oneline HEAD..@{upstream}", description: "Commits anzeigen, die lokal noch fehlen" },
|
||
{ command: "git log --oneline @{upstream}..HEAD", description: "Noch nicht veröffentlichte lokale Commits anzeigen" },
|
||
{ command: "git diff HEAD...@{upstream}", description: "Änderungen seit dem gemeinsamen Ausgangspunkt vergleichen" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-force",
|
||
title: "Force Push verstehen",
|
||
summary: "Nach einem Rebase stimmt die lokale Historie nicht mehr mit dem Remote überein. --force-with-lease überschreibt nur, wenn niemand den Remote-Branch seit deinem letzten Fetch verändert hat.",
|
||
commands: [
|
||
{ command: "git push --force-with-lease", description: "Rebaseten Branch mit Schutz vor fremden neuen Commits aktualisieren" },
|
||
],
|
||
note: "Verwende niemals blind --force auf gemeinsam genutzten Branches. Bevorzuge --force-with-lease und stimme das Umschreiben der Historie im Team ab.",
|
||
},
|
||
);
|
||
|
||
deCategories.find((category) => category.id === "troubleshooting")?.sections.push(
|
||
{
|
||
id: "trouble-undo-map",
|
||
title: "Restore, Reset und Revert unterscheiden",
|
||
summary: "Die drei Befehle lösen verschiedene Probleme: Restore betrifft Dateien, Reset verschiebt Branch/Index, Revert macht veröffentlichte Änderungen durch einen neuen Commit rückgängig.",
|
||
commands: [
|
||
{ command: "git restore <datei>", description: "Nicht gestagte Dateiänderungen verwerfen" },
|
||
{ command: "git restore --staged <datei>", description: "Staging rückgängig machen, Dateiänderung behalten" },
|
||
{ command: "git reset --soft HEAD~1", description: "Letzten lokalen Commit entfernen, alles gestaged behalten" },
|
||
{ command: "git revert <commit>", description: "Veröffentlichten Commit sicher durch Gegen-Commit umkehren" },
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-errors",
|
||
title: "Typische Fehlermeldungen",
|
||
summary: "Git-Fehler beschreiben meist den blockierenden Zustand. Prüfe zuerst status, Branch, Upstream und laufende Operationen, bevor du Befehle wiederholst.",
|
||
steps: [
|
||
"non-fast-forward: Im Remote existieren Commits, die lokal fehlen. Fetch, vergleichen und integrieren.",
|
||
"detached HEAD: Du bist direkt auf einem Commit. Erstelle einen Branch, wenn du neue Arbeit behalten willst.",
|
||
"pathspec did not match: Pfad oder Branch-Name ist falsch oder lokal noch nicht vorhanden. Prüfe Schreibweise und Fetch-Stand.",
|
||
"local changes would be overwritten: Committe, stash oder verwerfe die genannten Änderungen vor Checkout/Pull.",
|
||
"not a git repository: Aktueller Ordner liegt außerhalb eines Repositorys oder .git fehlt.",
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-diagnose",
|
||
title: "Diagnose ohne weitere Schäden",
|
||
summary: "Bevor du Reset, Clean oder Force verwendest, sichere den aktuellen Zustand und sammle lesende Informationen.",
|
||
commands: [
|
||
{ command: "git status", description: "Aktuellen Zustand und Handlungsanweisungen anzeigen" },
|
||
{ command: "git diff && git diff --staged", description: "Ungesicherte und gestagte Änderungen vollständig prüfen" },
|
||
{ command: "git branch backup/before-recovery", description: "Aktuellen Commit mit einem Sicherungs-Branch verankern" },
|
||
{ command: "git stash push -u -m \"backup before recovery\"", description: "Auch ungetrackte lokale Arbeit vorübergehend sichern" },
|
||
],
|
||
note: "git clean -fd und git reset --hard können nicht getrackte beziehungsweise lokale Daten endgültig löschen. Nutze zuerst Vorschau, Backup-Branch oder Stash.",
|
||
},
|
||
);
|
||
|
||
deCategories.push(
|
||
{
|
||
id: "workflows",
|
||
label: "Praxis-Workflows",
|
||
description: "Bewährte Rezepte für typische Aufgaben vom Feature bis zum Hotfix.",
|
||
sections: [
|
||
{
|
||
id: "workflow-feature",
|
||
title: "Feature-Branch von Anfang bis Ende",
|
||
summary: "Dieser Ablauf hält den Branch aktuell, den Commit-Verlauf verständlich und die Integration überschaubar.",
|
||
commands: [
|
||
{ command: "git switch main && git pull --ff-only", description: "Aktuellen, unveränderten Ausgangspunkt herstellen" },
|
||
{ command: "git switch -c feature/<name>", description: "Neuen Feature-Branch erstellen" },
|
||
{ command: "git push -u origin HEAD", description: "Branch veröffentlichen und Upstream setzen" },
|
||
{ command: "git fetch origin && git rebase origin/main", description: "Vor Review auf aktuellen main setzen" },
|
||
],
|
||
steps: [
|
||
"Arbeite in kleinen Commits und prüfe vor jedem Commit den Staged-Diff.",
|
||
"Pushe regelmäßig als Sicherung und für Zusammenarbeit.",
|
||
"Führe Tests nach der letzten Synchronisierung aus.",
|
||
"Erstelle Review/PR, integriere nach Freigabe und lösche den Branch.",
|
||
],
|
||
},
|
||
{
|
||
id: "workflow-hotfix",
|
||
title: "Einzelnen Fix übernehmen",
|
||
summary: "Wenn ein bereits vorhandener Fix gezielt in einen Release-Branch muss, ist Cherry-pick oft präziser als ein vollständiger Merge.",
|
||
commands: [
|
||
{ command: "git switch release/<version>", description: "Ziel-Branch wechseln" },
|
||
{ command: "git pull --ff-only", description: "Sicherstellen, dass der Ziel-Branch aktuell ist" },
|
||
{ command: "git cherry-pick -x <fix-commit>", description: "Fix übernehmen und Herkunft in der Nachricht dokumentieren" },
|
||
],
|
||
note: "Prüfe, ob der Fix von früheren Commits abhängt. Ein technisch erfolgreicher Cherry-pick kann fachlich unvollständig sein.",
|
||
},
|
||
{
|
||
id: "workflow-clean-commit",
|
||
title: "Gemischte Änderungen in saubere Commits teilen",
|
||
summary: "Du musst nicht alles committen, was gerade geändert ist. Staging nach Hunk oder Zeile trennt Refactoring, Fix und Dokumentation.",
|
||
commands: [
|
||
{ command: "git add -p", description: "Änderungen abschnittsweise auswählen" },
|
||
{ command: "git diff --staged", description: "Ersten Commit-Inhalt prüfen" },
|
||
{ command: "git commit", description: "Ersten logischen Commit erstellen" },
|
||
{ command: "git add -p && git commit", description: "Mit dem nächsten Themenblock fortfahren" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "reference",
|
||
label: "Referenz & Glossar",
|
||
description: "Kompakte Befehlsübersicht und zentrale Git-Begriffe zum Nachschlagen.",
|
||
sections: [
|
||
{
|
||
id: "reference-daily",
|
||
title: "Tägliche Kurzreferenz",
|
||
summary: "Die häufigsten sicheren Befehle für Orientierung, Änderung, Commit und Synchronisierung.",
|
||
commands: [
|
||
{ command: "git status", description: "Zustand prüfen" },
|
||
{ command: "git diff", description: "Lokale Änderungen lesen" },
|
||
{ command: "git add -p", description: "Gezielt stagen" },
|
||
{ command: "git diff --staged", description: "Commit-Inhalt prüfen" },
|
||
{ command: "git commit", description: "Commit erstellen" },
|
||
{ command: "git fetch --prune", description: "Remote-Stand aktualisieren" },
|
||
{ command: "git push", description: "Lokale Commits veröffentlichen" },
|
||
],
|
||
},
|
||
{
|
||
id: "reference-glossary",
|
||
title: "Git-Glossar",
|
||
summary: "HEAD ist der aktuelle Checkout. Branches und Tags sind Referenzen auf Commits. origin ist nur der übliche Name eines Remotes. Upstream ist die zugeordnete Remote-Referenz eines lokalen Branches.",
|
||
steps: [
|
||
"Commit: Unveränderlicher Projekt-Schnappschuss mit Eltern, Autor, Zeit und Nachricht.",
|
||
"Index/Staging: Vorbereiteter Schnappschuss für den nächsten Commit.",
|
||
"Working tree: Ausgecheckte Dateien, die du gerade bearbeitest.",
|
||
"Remote: Benannte Verbindung zu einem anderen Repository, nicht automatisch „die Cloud“.",
|
||
"Fast-forward: Branch-Zeiger kann ohne Merge-Commit direkt nach vorn bewegt werden.",
|
||
"Detached HEAD: HEAD zeigt direkt auf einen Commit statt auf einen lokalen Branch.",
|
||
],
|
||
},
|
||
{
|
||
id: "reference-safety",
|
||
title: "Gefahrenstufen von Git-Befehlen",
|
||
summary: "Lesende Befehle wie status, log, show und diff sind unkritisch. Restore, Reset, Clean, Rebase und Force Push verändern oder löschen Zustand und verdienen eine zusätzliche Prüfung.",
|
||
steps: [
|
||
"Sicher lesend: status, log, show, diff, branch, remote -v, reflog.",
|
||
"Lokal verändernd: add, restore, commit, stash, switch, merge, rebase.",
|
||
"Potenziell datenlöschend: reset --hard, clean -fd, branch -D.",
|
||
"Teamweit riskant: push --force, veröffentlichte Commits rebasen oder Tags verschieben.",
|
||
],
|
||
note: "Wenn du unsicher bist: Stoppe, erstelle einen Backup-Branch und prüfe git status sowie git reflog. Git belohnt kleine, nachvollziehbare Schritte.",
|
||
},
|
||
],
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "start")?.sections.push(
|
||
{
|
||
id: "start-model",
|
||
title: "Understand Git's core model",
|
||
summary: "Git stores linked snapshots of your project rather than a running list of individual file edits. HEAD points to the current commit; the branch name moves forward when you commit.",
|
||
steps: [
|
||
"Working tree: The real files you edit. Changes are not part of a commit yet.",
|
||
"Staging area: The exact snapshot you are preparing for the next commit.",
|
||
"Local repository: Commits, branches, and tags initially exist only on your machine.",
|
||
"Remote repository: Push publishes commits, Fetch downloads references, and Pull downloads and integrates.",
|
||
],
|
||
note: "The staging area is not another folder. It is a Git snapshot that Gitty presents as “Staged”.",
|
||
},
|
||
{
|
||
id: "start-before-work",
|
||
title: "Checklist before every task",
|
||
summary: "A short state check prevents most accidental commits and complicated conflicts.",
|
||
commands: [
|
||
{ command: "git status --short --branch", description: "Check branch, upstream, and changes concisely" },
|
||
{ command: "git fetch --prune", description: "Refresh remote state without changing files" },
|
||
{ command: "git log --oneline --decorate -10", description: "Review the latest ten commits and references" },
|
||
],
|
||
steps: [
|
||
"Confirm that you are on the correct branch.",
|
||
"Commit or stash unfinished changes before switching branches.",
|
||
"Fetch remote information, then choose deliberately between Pull, Rebase, and Merge.",
|
||
],
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "app")?.sections.push(
|
||
{
|
||
id: "app-repositories",
|
||
title: "Manage repositories and tabs",
|
||
summary: "Repository Management groups open, recent, and favorite projects. Every open repository gets a tab with its branch and change status.",
|
||
steps: [
|
||
"Browse opens an existing local repository; Clone downloads a remote repository into a new folder.",
|
||
"Favorite frequently used projects so they remain visible independently of recent history.",
|
||
"Switch between projects with tabs. Gitty keeps useful repository state available during the session.",
|
||
"Use the tab context menu to remove a repository from the workspace without deleting its files.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-commit-detail",
|
||
title: "Create clean commits in Gitty",
|
||
summary: "A good commit contains one logical change and can be explained, reviewed, and reverted independently.",
|
||
steps: [
|
||
"Review Unstaged and the complete diff of every affected file.",
|
||
"Stage only related files, hunks, or lines. Tests and implementation may belong together; unrelated formatting usually does not.",
|
||
"Review the Staged diff by itself—this is exactly what will be committed.",
|
||
"Write a short imperative subject, for example “Handle expired credentials”.",
|
||
"Use Amend only while nobody else depends on the latest commit.",
|
||
],
|
||
note: "If the Staged diff contains a surprise, unstage it. A commit is the wrong place for “it will probably be fine”.",
|
||
},
|
||
{
|
||
id: "app-branches-tags",
|
||
title: "Branches and tags in the app",
|
||
summary: "The Branch panel shows local and remote branches plus Ahead/Behind. Its context menu supports switch, create, rename, delete, merge, and rebase actions.",
|
||
steps: [
|
||
"Create a branch from the current HEAD or from a specific commit in History.",
|
||
"Checkout/Switch updates the working tree and HEAD. Save incompatible local changes first.",
|
||
"Tags mark fixed commits, usually releases. A tag does not move forward automatically.",
|
||
"Remote branches are references. Switching creates a local tracking branch when needed.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-history-tools",
|
||
title: "History, file history, Blame, and Restore",
|
||
summary: "Gitty connects the commit graph, file history, and restoration tools so you can understand how and why a change evolved.",
|
||
steps: [
|
||
"Select a file in Explorer to view its history separately from the project history.",
|
||
"Blame links every current line to its latest modifying commit. Use it as a starting point, not as an accusation.",
|
||
"Compare shows the difference between any two commits or branch tips.",
|
||
"Restore from commit writes an older file version into the working tree. Review and commit the result normally.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-search",
|
||
title: "Find code origins with Global Search",
|
||
summary: "Code Search examines commit history to find where text or a function was introduced. File Search combines path search with file history.",
|
||
steps: [
|
||
"Search for a stable, distinctive excerpt rather than a common single line.",
|
||
"Enable case sensitivity only when it meaningfully reduces results.",
|
||
"Open a result as a diff to review the introduction in the full commit context.",
|
||
"For renames, Gitty shows old and new paths when Git can infer the similarity.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-operations",
|
||
title: "Finish active Git operations safely",
|
||
summary: "During Rebase or Cherry-pick, Gitty displays a dedicated state. Resolve every conflict, then choose deliberately between Continue and Abort.",
|
||
steps: [
|
||
"Resolve opens each conflict with Current, Incoming, and editable result content.",
|
||
"Mark a file resolved only after reviewing its meaning; removing conflict markers is not enough.",
|
||
"Continue processes the next commit and may reveal additional conflicts.",
|
||
"Abort restores the state from before the entire operation began.",
|
||
],
|
||
},
|
||
{
|
||
id: "app-credentials-settings",
|
||
title: "Credentials, AI, and settings",
|
||
summary: "Gitty requests credentials only for authenticated remote actions. Theme, language, and anonymous analytics live in Settings; AI providers are configured separately.",
|
||
steps: [
|
||
"Use a personal access token instead of the account password for HTTPS remotes.",
|
||
"Limit token permissions and lifetime to the minimum required.",
|
||
"AI-generated commit messages are suggestions: verify content, sensitive data, and the actual Staged diff.",
|
||
"Analytics never sends repository paths, remotes, branches, commit messages, file names, diffs, credentials, or code.",
|
||
],
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "basics")?.sections.push(
|
||
{
|
||
id: "basics-config",
|
||
title: "Identity and configuration",
|
||
summary: "Git writes your name and email into every commit. Global values apply to all repositories; local values override them only in the current project.",
|
||
commands: [
|
||
{ command: "git config --global user.name \"Ada Lovelace\"", description: "Set the global display name" },
|
||
{ command: "git config --global user.email \"ada@example.com\"", description: "Set the global commit email" },
|
||
{ command: "git config --list --show-origin", description: "Show effective settings and their source files" },
|
||
{ command: "git config user.email \"work@example.com\"", description: "Set email only for the current repository" },
|
||
],
|
||
},
|
||
{
|
||
id: "basics-ignore",
|
||
title: "Use .gitignore correctly",
|
||
summary: ".gitignore prevents new untracked files from being suggested. It does not automatically remove files that Git already tracks.",
|
||
commands: [
|
||
{ command: "git check-ignore -v <file>", description: "Show which ignore rule applies to a file" },
|
||
{ command: "git rm --cached <file>", description: "Remove a file from Git while keeping it locally" },
|
||
{ command: "git status --ignored", description: "Include ignored files in status output" },
|
||
],
|
||
note: "Never commit secrets. .gitignore prevents future tracking but does not remove secrets from existing commits.",
|
||
},
|
||
{
|
||
id: "basics-show",
|
||
title: "Inspect commits and objects",
|
||
summary: "Hashes identify Git objects. The first unique characters are usually enough; references such as HEAD~1 or main are readable pointers to commits.",
|
||
commands: [
|
||
{ command: "git show <commit>", description: "Show a commit's metadata and patch" },
|
||
{ command: "git show <commit>:<path>", description: "Print a file from a specific commit" },
|
||
{ command: "git diff <from>..<to>", description: "Compare two states directly" },
|
||
{ command: "git log --follow -- <file>", description: "Follow file history across renames" },
|
||
],
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "app")?.sections.push(
|
||
{
|
||
id: "app-line-staging",
|
||
title: "Stage changes line by line",
|
||
summary: "The patch dialog lets you prepare individual added and deleted lines instead of staging an entire file or hunk.",
|
||
steps: [
|
||
"Open the patch of a changed text file in Unstaged.",
|
||
"Click the selector in front of a green or red line. Shift-click selects a continuous range.",
|
||
"The selection bar shows the number of selected lines. Stage selected moves only that selection into Staged.",
|
||
"Open the same dialog under Staged and use Unstage selected to return individual lines to the working tree.",
|
||
"Review the Staged diff afterward. It is the authoritative preview of the next commit.",
|
||
],
|
||
note: "Git usually represents one edited line as one deletion plus one addition. Select both sides when the complete replacement belongs in the same commit.",
|
||
},
|
||
{
|
||
id: "app-line-discard",
|
||
title: "Discard hunks or individual lines",
|
||
summary: "Discard selected removes only selected lines; Discard Hunk removes the complete section. Gitty shows a confirmation before deleting changes.",
|
||
steps: [
|
||
"Check whether the dialog currently shows Staged changes or Unstaged changes.",
|
||
"Select exactly the lines that should not be kept.",
|
||
"Review the file path and scope in the confirmation, then confirm.",
|
||
"Refresh the patch if another application changed the file while the dialog was open.",
|
||
],
|
||
note: "Caution: discarded work cannot always be recovered. Commit or stash important intermediate work first.",
|
||
},
|
||
{
|
||
id: "app-package-updates",
|
||
title: "Install and update Gitty on Arch Linux",
|
||
summary: "The gitty-desktop AUR package downloads the public Gitea release and builds Gitty locally from source.",
|
||
commands: [
|
||
{ command: "yay -S gitty-desktop", description: "Build, install, or update Gitty from the AUR with yay" },
|
||
{ command: "paru -S gitty-desktop", description: "Alternatively build, install, or update Gitty with paru" },
|
||
{ command: "git clone https://aur.archlinux.org/gitty-desktop.git && cd gitty-desktop && makepkg -si", description: "Inspect and build the AUR package without an AUR helper" },
|
||
{ command: "pacman -Qi gitty-desktop", description: "Show the installed version and package information" },
|
||
],
|
||
steps: [
|
||
"PKGBUILD downloads the public source archive for the corresponding Gitea tag.",
|
||
"The pipeline updates the version, checksum, and .SRCINFO in the gitty-desktop AUR package.",
|
||
"AUR helpers detect new versions and turn the recipe into a regular Pacman package locally.",
|
||
"Without an AUR helper, clone the AUR Git repository, inspect its files, and run makepkg -si.",
|
||
],
|
||
note: "AUR packages are user-maintained. Review PKGBUILD and .SRCINFO before installing, especially after major changes.",
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "branches")?.sections.push(
|
||
{
|
||
id: "branches-worktrees-concept",
|
||
title: "What a worktree is",
|
||
summary: "One repository has a shared Git database but can have multiple working folders. Each worktree has its own checked-out files and usually its own branch; commits and references are immediately visible from every worktree.",
|
||
steps: [
|
||
"The main worktree is the folder you originally opened as the repository.",
|
||
"An additional worktree is not a full clone: Git objects and history are shared while the working folder and index remain separate.",
|
||
"File changes stay inside their worktree. A commit created there immediately becomes part of the shared repository.",
|
||
"Worktrees are useful for parallel features, urgent hotfixes, reviews, or long-running builds on another branch.",
|
||
"Use a separate clone when you need independent remotes, configuration, or isolated experiments.",
|
||
],
|
||
note: "A branch normally cannot be checked out in two worktrees at once. This prevents two folders from changing the same branch inconsistently.",
|
||
},
|
||
{
|
||
id: "branches-worktrees",
|
||
title: "Open multiple branches in parallel with worktrees",
|
||
summary: "A worktree checks out another branch into a separate folder. This lets you work on multiple tasks without switching the main folder or stashing local changes.",
|
||
steps: [
|
||
"Open Worktrees below Tags in the Branch panel.",
|
||
"Choose Existing branch for a local branch, New branch to create one, or Detached for a fixed commit without a branch.",
|
||
"Choose an empty destination folder and create the worktree. Open tab then opens it as a separate repository tab.",
|
||
"Move relocates an inactive worktree. Lock protects it from accidental pruning or removal, for example while stored on an external drive.",
|
||
"Remove deletes the worktree folder and registration but keeps its branch. Prune removes only stale registrations for missing folders.",
|
||
],
|
||
commands: [
|
||
{ command: "git worktree list", description: "Show all registered worktrees" },
|
||
{ command: "git worktree add ../project-fix fix/login", description: "Check out an existing branch in a new folder" },
|
||
{ command: "git worktree add -b feature/name ../project-feature main", description: "Create a branch and worktree from main" },
|
||
{ command: "git worktree prune --dry-run", description: "Preview stale registrations before pruning" },
|
||
],
|
||
note: "A branch can normally be checked out in only one worktree at a time. Commit, stash, or deliberately force removal of local changes before removing a worktree.",
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "branches")?.sections.push(
|
||
{
|
||
id: "branches-strategy",
|
||
title: "A simple branch strategy",
|
||
summary: "Short, focused branches reduce conflicts. Update them regularly and integrate them soon after review.",
|
||
steps: [
|
||
"Start from an up-to-date main and use a descriptive name such as feature/help-search.",
|
||
"Commit small understandable units and push the branch for backup and review.",
|
||
"Synchronize with the target branch before completion and resolve conflicts on your branch.",
|
||
"Merge after review and delete the short-lived branch locally and remotely.",
|
||
],
|
||
},
|
||
{
|
||
id: "branches-cherry-pick",
|
||
title: "Use Cherry-pick deliberately",
|
||
summary: "Cherry-pick copies the change from an existing commit as a new commit on the current branch. It is useful for individual fixes but not a replacement for normal branch integration.",
|
||
commands: [
|
||
{ command: "git cherry-pick <commit>", description: "Copy one commit onto the current branch" },
|
||
{ command: "git cherry-pick --no-commit <commit>", description: "Apply the change but edit it before committing" },
|
||
{ command: "git cherry-pick --continue", description: "Continue after conflict resolution" },
|
||
{ command: "git cherry-pick --abort", description: "Abort the complete cherry-pick" },
|
||
],
|
||
},
|
||
{
|
||
id: "branches-interactive-rebase",
|
||
title: "Interactive Rebase",
|
||
summary: "Before publishing, you can reorder, rename, combine, or remove local commits. Gitty provides a visual rebase plan.",
|
||
steps: [
|
||
"Pick keeps a commit, Reword changes its message, Squash/Fixup combines it with the previous commit, and Drop removes it.",
|
||
"Order dependencies so each intermediate step remains as understandable and buildable as possible.",
|
||
"After rebasing, run tests and review commit order and the final diff against the target branch.",
|
||
],
|
||
note: "Interactive Rebase creates new commit hashes. Prefer it for your own commits that are not yet shared.",
|
||
},
|
||
{
|
||
id: "branches-tags",
|
||
title: "Mark releases with tags",
|
||
summary: "An annotated tag also stores author, date, and message, making it better for releases than a lightweight tag.",
|
||
commands: [
|
||
{ command: "git tag -a v1.2.0 -m \"Release 1.2.0\"", description: "Create an annotated release tag" },
|
||
{ command: "git show v1.2.0", description: "Inspect the tag and referenced commit" },
|
||
{ command: "git push origin v1.2.0", description: "Publish a specific tag" },
|
||
{ command: "git push origin --tags", description: "Publish all missing local tags" },
|
||
],
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "remote")?.sections.push(
|
||
{
|
||
id: "remote-tracking",
|
||
title: "Tracking branches and upstream",
|
||
summary: "An upstream connects a local branch with its remote reference. This tells Pull, Push, and Ahead/Behind which two histories to compare.",
|
||
commands: [
|
||
{ command: "git branch --show-current", description: "Show the current local branch" },
|
||
{ command: "git branch -u origin/<branch>", description: "Set the upstream of the current branch" },
|
||
{ command: "git branch -vv", description: "Show upstream and Ahead/Behind for local branches" },
|
||
{ command: "git push -u origin HEAD", description: "Publish the current branch and set its upstream" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-safe-pull",
|
||
title: "Synchronize safely",
|
||
summary: "Fetch is the most controlled first step. You can then inspect the difference and deliberately choose Merge or Rebase.",
|
||
commands: [
|
||
{ command: "git fetch origin", description: "Download remote information without changing the local branch" },
|
||
{ command: "git log --oneline HEAD..@{upstream}", description: "Show commits that are still missing locally" },
|
||
{ command: "git log --oneline @{upstream}..HEAD", description: "Show unpublished local commits" },
|
||
{ command: "git diff HEAD...@{upstream}", description: "Compare changes since the common ancestor" },
|
||
],
|
||
},
|
||
{
|
||
id: "remote-force",
|
||
title: "Understand Force Push",
|
||
summary: "After rebasing, local history no longer matches the remote. --force-with-lease overwrites only if nobody changed the remote branch since your latest Fetch.",
|
||
commands: [
|
||
{ command: "git push --force-with-lease", description: "Update a rebased branch while protecting others' new commits" },
|
||
],
|
||
note: "Never use --force blindly on shared branches. Prefer --force-with-lease and coordinate history rewrites with the team.",
|
||
},
|
||
);
|
||
|
||
enCategories.find((category) => category.id === "troubleshooting")?.sections.push(
|
||
{
|
||
id: "trouble-undo-map",
|
||
title: "Distinguish Restore, Reset, and Revert",
|
||
summary: "The commands solve different problems: Restore changes files, Reset moves a branch or the index, and Revert undoes published changes through a new commit.",
|
||
commands: [
|
||
{ command: "git restore <file>", description: "Discard unstaged file changes" },
|
||
{ command: "git restore --staged <file>", description: "Undo staging while keeping the file change" },
|
||
{ command: "git reset --soft HEAD~1", description: "Remove the latest local commit and keep everything staged" },
|
||
{ command: "git revert <commit>", description: "Safely undo a published commit with an inverse commit" },
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-errors",
|
||
title: "Common error messages",
|
||
summary: "Git errors usually describe the blocking state. Check status, branch, upstream, and active operations before repeating commands.",
|
||
steps: [
|
||
"non-fast-forward: The remote contains commits missing locally. Fetch, compare, and integrate them.",
|
||
"detached HEAD: You are directly on a commit. Create a branch if you want to keep new work.",
|
||
"pathspec did not match: The path or branch name is wrong or not available locally. Check spelling and Fetch state.",
|
||
"local changes would be overwritten: Commit, stash, or discard the listed changes before Checkout or Pull.",
|
||
"not a git repository: The current folder is outside a repository or its .git data is missing.",
|
||
],
|
||
},
|
||
{
|
||
id: "trouble-diagnose",
|
||
title: "Diagnose without causing more damage",
|
||
summary: "Before using Reset, Clean, or Force, preserve the current state and collect read-only information.",
|
||
commands: [
|
||
{ command: "git status", description: "Show the current state and Git's suggested next steps" },
|
||
{ command: "git diff && git diff --staged", description: "Review unstaged and staged changes completely" },
|
||
{ command: "git branch backup/before-recovery", description: "Anchor the current commit with a backup branch" },
|
||
{ command: "git stash push -u -m \"backup before recovery\"", description: "Temporarily protect tracked and untracked work" },
|
||
],
|
||
note: "git clean -fd and git reset --hard can permanently remove untracked or local data. Prefer a preview, backup branch, or stash first.",
|
||
},
|
||
);
|
||
|
||
enCategories.push(
|
||
{
|
||
id: "workflows",
|
||
label: "Practical workflows",
|
||
description: "Reliable recipes for common tasks from feature work to hotfixes.",
|
||
sections: [
|
||
{
|
||
id: "workflow-feature",
|
||
title: "Feature branch from start to finish",
|
||
summary: "This workflow keeps your branch current, the commit history understandable, and integration manageable.",
|
||
commands: [
|
||
{ command: "git switch main && git pull --ff-only", description: "Establish a current, unchanged starting point" },
|
||
{ command: "git switch -c feature/<name>", description: "Create a new feature branch" },
|
||
{ command: "git push -u origin HEAD", description: "Publish the branch and set its upstream" },
|
||
{ command: "git fetch origin && git rebase origin/main", description: "Move onto the latest main before review" },
|
||
],
|
||
steps: [
|
||
"Work in small commits and review the Staged diff before every commit.",
|
||
"Push regularly for backup and collaboration.",
|
||
"Run tests after the final synchronization.",
|
||
"Open a review or PR, integrate after approval, and delete the branch.",
|
||
],
|
||
},
|
||
{
|
||
id: "workflow-hotfix",
|
||
title: "Apply one isolated fix",
|
||
summary: "When an existing fix must be applied to a release branch, Cherry-pick is often more precise than merging a complete branch.",
|
||
commands: [
|
||
{ command: "git switch release/<version>", description: "Switch to the target branch" },
|
||
{ command: "git pull --ff-only", description: "Ensure the target branch is current" },
|
||
{ command: "git cherry-pick -x <fix-commit>", description: "Apply the fix and record its origin in the message" },
|
||
],
|
||
note: "Check whether the fix depends on earlier commits. A technically successful Cherry-pick can still be functionally incomplete.",
|
||
},
|
||
{
|
||
id: "workflow-clean-commit",
|
||
title: "Split mixed changes into clean commits",
|
||
summary: "You do not have to commit everything that is currently changed. Hunk or line staging separates refactoring, fixes, and documentation.",
|
||
commands: [
|
||
{ command: "git add -p", description: "Select changes hunk by hunk" },
|
||
{ command: "git diff --staged", description: "Review the first commit's content" },
|
||
{ command: "git commit", description: "Create the first logical commit" },
|
||
{ command: "git add -p && git commit", description: "Continue with the next topic" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "reference",
|
||
label: "Reference & glossary",
|
||
description: "A compact command overview and the core Git terms in one place.",
|
||
sections: [
|
||
{
|
||
id: "reference-daily",
|
||
title: "Daily quick reference",
|
||
summary: "The most common safe commands for orientation, changes, commits, and synchronization.",
|
||
commands: [
|
||
{ command: "git status", description: "Check state" },
|
||
{ command: "git diff", description: "Read local changes" },
|
||
{ command: "git add -p", description: "Stage selectively" },
|
||
{ command: "git diff --staged", description: "Review commit contents" },
|
||
{ command: "git commit", description: "Create a commit" },
|
||
{ command: "git fetch --prune", description: "Refresh remote state" },
|
||
{ command: "git push", description: "Publish local commits" },
|
||
],
|
||
},
|
||
{
|
||
id: "reference-glossary",
|
||
title: "Git glossary",
|
||
summary: "HEAD is the current checkout. Branches and tags are references to commits. origin is only the conventional name of a remote. Upstream is the remote reference assigned to a local branch.",
|
||
steps: [
|
||
"Commit: Immutable project snapshot with parents, author, time, and message.",
|
||
"Index/Staging: Prepared snapshot for the next commit.",
|
||
"Working tree: Checked-out files you are currently editing.",
|
||
"Remote: Named connection to another repository, not necessarily “the cloud”.",
|
||
"Fast-forward: A branch pointer can move forward without a merge commit.",
|
||
"Detached HEAD: HEAD points directly to a commit instead of a local branch.",
|
||
],
|
||
},
|
||
{
|
||
id: "reference-safety",
|
||
title: "Risk levels of Git commands",
|
||
summary: "Read-only commands such as status, log, show, and diff are harmless. Restore, Reset, Clean, Rebase, and Force Push change or remove state and deserve an extra check.",
|
||
steps: [
|
||
"Safe and read-only: status, log, show, diff, branch, remote -v, reflog.",
|
||
"Locally modifying: add, restore, commit, stash, switch, merge, rebase.",
|
||
"Potentially destructive: reset --hard, clean -fd, branch -D.",
|
||
"Team-wide risk: push --force, rebasing published commits, or moving tags.",
|
||
],
|
||
note: "When unsure, stop, create a backup branch, and inspect git status and git reflog. Git rewards small, understandable steps.",
|
||
},
|
||
],
|
||
},
|
||
);
|
||
|
||
deCategories.splice(1, 0, {
|
||
id: "changelog",
|
||
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",
|
||
summary: "Dieses Release erweitert Gitty um eine AI-gestützte Aufteilung gestagter Änderungen in logisch getrennte Commits.",
|
||
steps: [
|
||
"AI-Commit-Aufteilung: Der gestagte Diff wird analysiert und als geordneter Plan aus mehreren logisch zusammengehörenden Commits vorgeschlagen.",
|
||
"Für jede Gruppe wird automatisch eine editierbare Conventional-Commit-Nachricht erzeugt.",
|
||
"Dateien können vor dem Commit zwischen den vorgeschlagenen Gruppen verschoben werden.",
|
||
"Mit „Commit all“ werden alle bestätigten Gruppen sicher und der Reihe nach committed.",
|
||
"Der Dialog erklärt leere Gruppen oder fehlende Nachrichten und schützt vor einem zwischenzeitlich veränderten Staging-Bereich.",
|
||
"„Commit all“ reagiert wieder zuverlässig und bricht nicht mehr beim Kopieren des reaktiven Dialogzustands ab.",
|
||
"Ein geschlossenes aktives Repository kann nicht mehr durch einen verspäteten Status- oder Fetch-Request erneut geöffnet werden.",
|
||
],
|
||
note: "Die AI-Commit-Aufteilung unterstützt OpenAI, Anthropic und eigene OpenAI-kompatible Endpunkte. In Paketdateien erscheint diese Version als 2026.7.22.",
|
||
},
|
||
{
|
||
id: "changelog-2026-07-21",
|
||
title: "Version 2026.07.21",
|
||
summary: "Dieses Release bündelt paralleles Arbeiten mit Worktrees, präzisere Commits und die neue Arch-Linux-Verteilung.",
|
||
steps: [
|
||
"Worktree-Verwaltung: zusätzliche Arbeitsordner erstellen, öffnen, verschieben, sperren, entsperren, reparieren, entfernen und veraltete Registrierungen aufräumen.",
|
||
"Worktrees sind direkt über den neuen Reiter unter Tags erreichbar; Branches lassen sich außerdem aus ihrem Kontextmenü in einem Worktree öffnen.",
|
||
"Zeilenweises Staging: einzelne Ergänzungen und Löschungen auswählen, per Shift-Klick Bereiche markieren sowie ausgewählte Zeilen stagen, unstagen oder verwerfen.",
|
||
"Sicherere Dialoge: verständlichere Branch-Löschabfrage und weichgezeichneter Hintergrund bei geöffneten Dialogen.",
|
||
"Arch-Linux-Pakete: automatisierter Build aus dem PKGBUILD, Veröffentlichung von .pkg.tar.zst und Repository-Datenbank für Pacman auf dem CDN.",
|
||
"Robustere Remote-Aktionen, zentrale Fehlermeldungen und strukturierte, datensparsame Telemetrie.",
|
||
"Erweiterte zweisprachige Hilfe mit Worktree-, Pacman- und Line-Staging-Anleitungen.",
|
||
],
|
||
note: "In Paketdateien kann dieselbe Version als 2026.7.21 erscheinen, weil Paketmanager numerische Versionssegmente ohne führende Null verwenden.",
|
||
},
|
||
{
|
||
id: "changelog-2026-7-20",
|
||
title: "Version 2026.7.20",
|
||
summary: "Der letzte veröffentlichte Stand konzentrierte sich auf produktiveres Arbeiten, bessere Orientierung und einen stabileren Paket-Build.",
|
||
steps: [
|
||
"Vor dem Commit kann eine AI-gestützte Codeprüfung den Staged-Diff analysieren.",
|
||
"Automatische Aktualisierung hält Repository-Status und Arbeitsbereich auf Wunsch aktuell.",
|
||
"Repository-Aktionsleiste, Statusdarstellung, Tabs und Diff-Ansicht wurden übersichtlicher gestaltet.",
|
||
"Die integrierte Hilfe wurde um ausführliche deutsche Git-Dokumentation ergänzt.",
|
||
"PKGBUILD und Build-Skripte wurden für die Arch-Linux-Verteilung vorbereitet.",
|
||
],
|
||
},
|
||
],
|
||
});
|
||
|
||
enCategories.splice(1, 0, {
|
||
id: "changelog",
|
||
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",
|
||
summary: "This release adds AI-assisted splitting of staged changes into separate logical commits.",
|
||
steps: [
|
||
"AI commit splitting analyzes the staged diff and proposes an ordered plan of logically related commits.",
|
||
"Every group receives an automatically generated, editable Conventional Commit message.",
|
||
"Files can be moved between proposed groups before committing.",
|
||
"Commit all safely creates every accepted group in sequence.",
|
||
"The dialog explains empty groups or missing messages and protects against a staging area that changed after planning.",
|
||
"Commit all now responds reliably instead of failing while copying reactive dialog state.",
|
||
"Closing the active repository no longer lets a delayed status or fetch request reopen the closed tab.",
|
||
],
|
||
note: "AI commit splitting supports OpenAI, Anthropic, and custom OpenAI-compatible endpoints. Package metadata represents this version as 2026.7.22.",
|
||
},
|
||
{
|
||
id: "changelog-2026-07-21",
|
||
title: "Version 2026.07.21",
|
||
summary: "This release combines parallel worktree workflows, more precise commits, and the new Arch Linux distribution.",
|
||
steps: [
|
||
"Worktree management: create, open, move, lock, unlock, repair, remove, and prune additional working folders.",
|
||
"Worktrees are available from the new entry below Tags; branches can also be opened in a worktree from their context menu.",
|
||
"Line-level staging: select additions and deletions, Shift-click ranges, and stage, unstage, or discard selected lines.",
|
||
"Safer dialogs: a clearer branch deletion confirmation and a blurred background while dialogs are open.",
|
||
"Arch Linux packages: automated PKGBUILD builds plus publication of .pkg.tar.zst and the Pacman repository database to the CDN.",
|
||
"More robust remote operations, centralized error messages, and structured privacy-conscious telemetry.",
|
||
"Expanded bilingual help for worktrees, Pacman installation, and line-level staging.",
|
||
],
|
||
note: "Package metadata may represent the same release as 2026.7.21 because package managers use numeric version segments without leading zeroes.",
|
||
},
|
||
{
|
||
id: "changelog-2026-7-20",
|
||
title: "Version 2026.7.20",
|
||
summary: "The latest published version focused on productivity, clearer navigation, and a more stable package build.",
|
||
steps: [
|
||
"AI-assisted pre-commit review can analyze the staged diff before committing.",
|
||
"Optional automatic refresh keeps repository status and the workspace current.",
|
||
"The repository action bar, status presentation, tabs, and diff view became easier to scan.",
|
||
"The built-in help gained comprehensive German Git documentation.",
|
||
"PKGBUILD and build scripts prepared the Arch Linux distribution workflow.",
|
||
],
|
||
},
|
||
],
|
||
});
|
||
|
||
let { language = "en", onClose = () => {} }: Props = $props();
|
||
const isGerman = $derived(language === "de");
|
||
const categories = $derived(isGerman ? deCategories : enCategories);
|
||
let selectedCategoryId = $state("start");
|
||
let searchQuery = $state("");
|
||
let copiedCommand = $state("");
|
||
let searchInput: HTMLInputElement;
|
||
let contentElement: HTMLElement;
|
||
let copyTimer: ReturnType<typeof setTimeout> | undefined;
|
||
|
||
const normalizedQuery = $derived(searchQuery.trim().toLocaleLowerCase(language));
|
||
const selectedCategory = $derived(categories.find((category) => category.id === selectedCategoryId) ?? categories[0]);
|
||
const visibleGroups = $derived.by(() => {
|
||
if (!normalizedQuery) return [{ category: selectedCategory, sections: selectedCategory.sections }];
|
||
return categories.flatMap((category) => {
|
||
const sections = category.sections.filter((section) => sectionSearchText(category, section).includes(normalizedQuery));
|
||
return sections.length > 0 ? [{ category, sections }] : [];
|
||
});
|
||
});
|
||
const resultCount = $derived(visibleGroups.reduce((sum, group) => sum + group.sections.length, 0));
|
||
|
||
$effect(() => {
|
||
normalizedQuery;
|
||
selectedCategoryId;
|
||
if (contentElement) contentElement.scrollTop = 0;
|
||
});
|
||
|
||
onMount(() => {
|
||
void tick().then(() => searchInput?.focus());
|
||
return () => {
|
||
if (copyTimer) clearTimeout(copyTimer);
|
||
};
|
||
});
|
||
|
||
function sectionSearchText(category: HelpCategory, section: HelpSection): string {
|
||
return [
|
||
category.label,
|
||
category.description,
|
||
section.title,
|
||
section.summary,
|
||
...(section.steps ?? []),
|
||
...(section.commands?.flatMap((entry) => [entry.command, entry.description]) ?? []),
|
||
section.note ?? "",
|
||
].join(" ").toLocaleLowerCase(language);
|
||
}
|
||
|
||
function selectCategory(id: string) {
|
||
selectedCategoryId = id;
|
||
searchQuery = "";
|
||
}
|
||
|
||
function isWarningNote(note: string): boolean {
|
||
return /^(Vorsicht|Rebase|Caution|Do not)/.test(note);
|
||
}
|
||
|
||
async function copyCommand(command: string) {
|
||
try {
|
||
await navigator.clipboard.writeText(command);
|
||
copiedCommand = command;
|
||
if (copyTimer) clearTimeout(copyTimer);
|
||
copyTimer = setTimeout(() => { copiedCommand = ""; }, 1800);
|
||
} catch {
|
||
copiedCommand = "";
|
||
}
|
||
}
|
||
|
||
function handleBackdropClick(event: MouseEvent) {
|
||
if (event.target === event.currentTarget) onClose();
|
||
}
|
||
</script>
|
||
|
||
<div class="help-backdrop" role="presentation" onclick={handleBackdropClick}>
|
||
<div class="help-overlay" role="dialog" aria-modal="true" aria-labelledby="help-title">
|
||
<header class="help-header">
|
||
<div class="help-title-wrap">
|
||
<span class="help-mark"><CircleHelp size={19} aria-hidden="true" /></span>
|
||
<div>
|
||
<h2 id="help-title">{isGerman ? "Gitty Hilfe" : "Gitty Help"}</h2>
|
||
<p>{isGerman ? "App-Anleitung und Git-Wissen an einem Ort" : "App guidance and Git knowledge in one place"}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<label class="help-search">
|
||
<span class="help-search-icon"><Search size={17} aria-hidden="true" /></span>
|
||
<input bind:this={searchInput} bind:value={searchQuery} type="search" placeholder={isGerman ? "Hilfe und Git-Befehle durchsuchen …" : "Search help and Git commands …"} aria-label={isGerman ? "Hilfe durchsuchen" : "Search help"} />
|
||
<kbd>Ctrl /</kbd>
|
||
</label>
|
||
|
||
<button class="help-close" type="button" onclick={onClose} title={isGerman ? "Hilfe schließen" : "Close help"} aria-label={isGerman ? "Hilfe schließen" : "Close help"}>
|
||
<X size={19} aria-hidden="true" />
|
||
</button>
|
||
</header>
|
||
|
||
<div class="help-layout">
|
||
<nav class="help-nav" aria-label={isGerman ? "Hilfethemen" : "Help topics"}>
|
||
<p class="help-nav-label">{isGerman ? "Themen" : "Topics"}</p>
|
||
{#each categories as category}
|
||
<button
|
||
type="button"
|
||
class:active={!normalizedQuery && category.id === selectedCategoryId}
|
||
onclick={() => selectCategory(category.id)}
|
||
>
|
||
<span class="help-nav-icon">
|
||
{#if category.id === "start"}<Home size={17} aria-hidden="true" />
|
||
{:else if category.id === "changelog"}<Sparkles size={17} aria-hidden="true" />
|
||
{:else if category.id === "app"}<BookOpen size={17} aria-hidden="true" />
|
||
{:else if category.id === "basics"}<GitCommitHorizontal size={17} aria-hidden="true" />
|
||
{:else if category.id === "branches"}<GitBranch size={17} aria-hidden="true" />
|
||
{:else if category.id === "remote"}<Cloud size={17} aria-hidden="true" />
|
||
{:else if category.id === "troubleshooting"}<Wrench size={17} aria-hidden="true" />
|
||
{:else if category.id === "workflows"}<ListChecks size={17} aria-hidden="true" />
|
||
{:else if category.id === "reference"}<Library size={17} aria-hidden="true" />
|
||
{:else}<Keyboard size={17} aria-hidden="true" />{/if}
|
||
</span>
|
||
<span>{category.label}</span>
|
||
<span class="help-nav-chevron"><ChevronRight size={14} aria-hidden="true" /></span>
|
||
</button>
|
||
{/each}
|
||
|
||
<div class="help-nav-tip">
|
||
<span class="help-tip-icon"><Lightbulb size={16} aria-hidden="true" /></span>
|
||
<span>{isGerman ? "Suche auch nach Befehlen wie" : "Try commands such as"} <code>rebase</code>, <code>stash</code> {isGerman ? "oder" : "or"} <code>reflog</code>.</span>
|
||
</div>
|
||
</nav>
|
||
|
||
<main class="help-content" bind:this={contentElement}>
|
||
{#if normalizedQuery}
|
||
<header class="help-result-header">
|
||
<div>
|
||
<span>{isGerman ? "Suchergebnisse" : "Search results"}</span>
|
||
<h3>{resultCount} {isGerman ? "Treffer für" : resultCount === 1 ? "result for" : "results for"} „{searchQuery.trim()}“</h3>
|
||
</div>
|
||
<button type="button" onclick={() => { searchQuery = ""; }}>{isGerman ? "Suche löschen" : "Clear search"}</button>
|
||
</header>
|
||
{/if}
|
||
|
||
{#if visibleGroups.length === 0}
|
||
<div class="help-empty">
|
||
<span class="help-empty-icon"><Search size={28} aria-hidden="true" /></span>
|
||
<h3>{isGerman ? "Kein Hilfethema gefunden" : "No help topic found"}</h3>
|
||
<p>{isGerman ? "Versuche einen allgemeineren Begriff wie „Commit“, „Branch“, „Remote“ oder „Konflikt“." : "Try a broader term such as “commit”, “branch”, “remote”, or “conflict”."}</p>
|
||
<button class="btn-secondary" type="button" onclick={() => { searchQuery = ""; }}>{isGerman ? "Alle Themen anzeigen" : "Show all topics"}</button>
|
||
</div>
|
||
{:else}
|
||
{#each visibleGroups as group}
|
||
<div class="help-group">
|
||
<header class="help-group-header">
|
||
<span>{normalizedQuery ? group.category.label : isGerman ? "Gitty Handbuch" : "Gitty handbook"}</span>
|
||
<h3>{normalizedQuery ? group.category.label : group.category.label}</h3>
|
||
<p>{group.category.description}</p>
|
||
</header>
|
||
|
||
{#each group.sections as section, sectionIndex}
|
||
<article class="help-section" id={section.id}>
|
||
<div class="help-section-number">{String(sectionIndex + 1).padStart(2, "0")}</div>
|
||
<div class="help-section-body">
|
||
<h4>{section.title}</h4>
|
||
<p>{section.summary}</p>
|
||
|
||
{#if section.steps}
|
||
<ol class="help-steps">
|
||
{#each section.steps as step}
|
||
<li><span>{step}</span></li>
|
||
{/each}
|
||
</ol>
|
||
{/if}
|
||
|
||
{#if section.commands}
|
||
<div class="help-commands">
|
||
{#each section.commands as entry}
|
||
<div class="help-command-row">
|
||
<code>{entry.command}</code>
|
||
<span>{entry.description}</span>
|
||
<button type="button" onclick={() => copyCommand(entry.command)} aria-label={(isGerman ? "Befehl kopieren: " : "Copy command: ") + entry.command} title={isGerman ? "Befehl kopieren" : "Copy command"}>
|
||
{#if copiedCommand === entry.command}
|
||
<Check size={15} aria-hidden="true" /><span>{isGerman ? "Kopiert" : "Copied"}</span>
|
||
{:else}
|
||
<Clipboard size={15} aria-hidden="true" /><span>{isGerman ? "Kopieren" : "Copy"}</span>
|
||
{/if}
|
||
</button>
|
||
</div>
|
||
{/each}
|
||
</div>
|
||
{/if}
|
||
|
||
{#if section.note}
|
||
<div class="help-note" class:warning={isWarningNote(section.note)}>
|
||
{#if isWarningNote(section.note)}
|
||
<span class="help-note-icon"><AlertTriangle size={16} aria-hidden="true" /></span>
|
||
{:else}
|
||
<span class="help-note-icon"><Lightbulb size={16} aria-hidden="true" /></span>
|
||
{/if}
|
||
<span>{section.note}</span>
|
||
</div>
|
||
{/if}
|
||
</div>
|
||
</article>
|
||
{/each}
|
||
</div>
|
||
{/each}
|
||
{/if}
|
||
</main>
|
||
</div>
|
||
|
||
<footer class="help-footer">
|
||
<span><Command size={14} aria-hidden="true" /> <kbd>Ctrl</kbd><kbd>/</kbd> {isGerman ? "Hilfe öffnen" : "open help"}</span>
|
||
<span><kbd>Esc</kbd> {isGerman ? "schließen" : "close"}</span>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.help-backdrop {
|
||
position: fixed;
|
||
inset: var(--app-titlebar-height, 42px) 0 0;
|
||
z-index: 70;
|
||
display: grid;
|
||
place-items: center;
|
||
padding: 24px;
|
||
background: rgba(3, 7, 13, 0.82);
|
||
backdrop-filter: blur(3px);
|
||
}
|
||
|
||
.help-overlay {
|
||
display: grid;
|
||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||
width: min(1240px, 100%);
|
||
height: min(820px, 100%);
|
||
overflow: hidden;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 12px;
|
||
color: var(--color-ink);
|
||
background: var(--app-dialog-bg);
|
||
box-shadow: var(--app-dialog-shadow);
|
||
}
|
||
|
||
.help-header {
|
||
display: grid;
|
||
grid-template-columns: minmax(220px, 0.72fr) minmax(320px, 1.2fr) minmax(44px, 0.72fr);
|
||
align-items: center;
|
||
gap: 22px;
|
||
min-height: 74px;
|
||
padding: 12px 16px 12px 20px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
background: var(--app-dialog-chrome);
|
||
}
|
||
|
||
.help-title-wrap { display: flex; align-items: center; gap: 11px; min-width: 0; }
|
||
.help-mark { display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid rgba(90, 140, 248, 0.28); border-radius: 8px; color: var(--color-accent); background: rgba(90, 140, 248, 0.09); }
|
||
.help-title-wrap h2 { margin: 0; font-size: 17px; line-height: 1.2; }
|
||
.help-title-wrap p { margin: 3px 0 0; overflow: hidden; color: var(--color-ink-faint); font-size: 10.5px; text-overflow: ellipsis; white-space: nowrap; }
|
||
|
||
.help-search { position: relative; display: flex; align-items: center; min-width: 0; }
|
||
.help-search-icon { position: absolute; left: 12px; display: grid; color: var(--color-ink-faint); pointer-events: none; }
|
||
.help-search input { width: 100%; height: 40px; padding: 0 68px 0 39px; border-color: var(--color-border-input); border-radius: 8px; background: var(--app-input-bg); color: var(--color-ink); font-size: 12.5px; }
|
||
.help-search kbd { position: absolute; right: 8px; }
|
||
.help-close { justify-self: end; width: 36px; min-height: 36px; padding: 0; border-color: transparent; border-radius: 7px; background: transparent; color: var(--color-ink-muted); }
|
||
|
||
.help-layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); min-height: 0; }
|
||
.help-nav { display: flex; flex-direction: column; min-height: 0; padding: 14px 10px 12px; border-right: 1px solid var(--color-border); background: var(--color-surface-dim); }
|
||
.help-nav-label { margin: 0 10px 8px; color: var(--color-ink-faint); font-size: 9.5px; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; }
|
||
.help-nav > button { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 8px; width: 100%; min-height: 42px; padding: 5px 9px; border-color: transparent; border-radius: 7px; background: transparent; color: var(--color-ink-muted); font-size: 11.5px; font-weight: 650; text-align: left; }
|
||
.help-nav > button:hover { background: var(--color-surface-hover); color: var(--color-ink); }
|
||
.help-nav > button.active { border-color: rgba(90, 140, 248, 0.22); background: rgba(90, 140, 248, 0.12); color: var(--color-accent); }
|
||
.help-nav-icon { display: grid; place-items: center; color: currentColor; }
|
||
.help-nav-chevron { display: grid; color: var(--color-ink-faint); }
|
||
.help-nav-tip { display: flex; align-items: flex-start; gap: 8px; margin: auto 6px 0; padding: 11px; border-top: 1px solid var(--color-border-subtle); color: var(--color-ink-faint); font-size: 10.5px; line-height: 1.5; }
|
||
.help-tip-icon { flex: 0 0 auto; display: grid; margin-top: 2px; color: #d8a13d; }
|
||
.help-nav-tip code { color: var(--color-ink-muted); font-family: var(--font-mono); }
|
||
|
||
.help-content { min-width: 0; min-height: 0; padding: 0 34px 48px; overflow: auto; outline: none; scroll-behavior: smooth; }
|
||
.help-group { max-width: 850px; margin: 0 auto; }
|
||
.help-group + .help-group { margin-top: 22px; border-top: 1px solid var(--color-border); }
|
||
.help-group-header { padding: 34px 0 26px; border-bottom: 1px solid var(--color-border); }
|
||
.help-group-header > span, .help-result-header span { color: var(--color-accent); font-size: 9.5px; font-weight: 800; letter-spacing: 0.11em; text-transform: uppercase; }
|
||
.help-group-header h3 { margin: 7px 0 8px; font-size: clamp(24px, 3vw, 32px); letter-spacing: -0.025em; }
|
||
.help-group-header p { max-width: 650px; margin: 0; color: var(--color-ink-muted); font-size: 13px; line-height: 1.55; }
|
||
|
||
.help-result-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; max-width: 850px; margin: 0 auto; padding: 22px 0 0; }
|
||
.help-result-header h3 { margin: 4px 0 0; font-size: 17px; }
|
||
.help-result-header button { min-height: 30px; padding: 0 10px; border-color: var(--color-border-subtle); background: transparent; color: var(--color-ink-muted); font-size: 10.5px; }
|
||
|
||
.help-section { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 2px; padding: 26px 0; border-bottom: 1px solid var(--color-border-subtle); }
|
||
.help-section-number { padding-top: 3px; color: var(--color-ink-faint); font-family: var(--font-mono); font-size: 10px; }
|
||
.help-section-body h4 { margin: 0 0 7px; font-size: 16px; }
|
||
.help-section-body > p { margin: 0; color: var(--color-ink-muted); font-size: 12.5px; line-height: 1.6; }
|
||
.help-steps { display: grid; gap: 10px; margin: 18px 0 0; padding: 0; list-style: none; counter-reset: help-step; }
|
||
.help-steps li { display: grid; grid-template-columns: 24px minmax(0, 1fr); align-items: start; gap: 9px; color: var(--color-ink-muted); font-size: 12px; line-height: 1.5; counter-increment: help-step; }
|
||
.help-steps li::before { content: counter(help-step); display: grid; place-items: center; width: 22px; height: 22px; border: 1px solid rgba(90, 140, 248, 0.35); border-radius: 50%; color: var(--color-accent); font-family: var(--font-mono); font-size: 9px; font-weight: 800; }
|
||
|
||
.help-commands { display: grid; gap: 1px; margin-top: 17px; overflow: hidden; border: 1px solid var(--color-border); border-radius: 8px; background: var(--color-border-subtle); }
|
||
.help-command-row { display: grid; grid-template-columns: minmax(210px, 0.9fr) minmax(190px, 1.2fr) auto; align-items: center; gap: 14px; min-height: 47px; padding: 6px 7px 6px 13px; background: var(--code-surface); }
|
||
.help-command-row:hover { background: var(--code-hover-bg); }
|
||
.help-command-row > code { overflow: hidden; color: var(--color-accent); font-family: var(--font-mono); font-size: 11.5px; text-overflow: ellipsis; white-space: nowrap; }
|
||
.help-command-row > span { color: var(--color-ink-faint); font-size: 10.5px; line-height: 1.35; }
|
||
.help-command-row button { display: inline-flex; align-items: center; gap: 6px; min-width: 83px; min-height: 31px; padding: 0 9px; border-color: var(--color-border-subtle); border-radius: 6px; background: var(--color-surface-raised); color: var(--color-ink-muted); font-size: 10px; }
|
||
.help-command-row button:hover { border-color: rgba(90, 140, 248, 0.32); color: var(--color-accent); }
|
||
|
||
.help-note { display: flex; align-items: flex-start; gap: 9px; margin-top: 15px; padding: 11px 12px; border: 1px solid rgba(90, 140, 248, 0.2); border-radius: 7px; background: rgba(90, 140, 248, 0.07); color: var(--color-ink-muted); font-size: 11px; line-height: 1.5; }
|
||
.help-note-icon { flex: 0 0 auto; display: grid; margin-top: 1px; color: var(--color-accent); }
|
||
.help-note.warning { border-color: rgba(224, 160, 64, 0.24); background: rgba(224, 160, 64, 0.07); }
|
||
.help-note.warning .help-note-icon { color: #d8a13d; }
|
||
|
||
.help-empty { display: grid; justify-items: center; max-width: 520px; margin: 90px auto 0; text-align: center; }
|
||
.help-empty-icon { display: grid; color: var(--color-ink-faint); }
|
||
.help-empty h3 { margin: 14px 0 5px; font-size: 18px; }
|
||
.help-empty p { margin: 0 0 18px; color: var(--color-ink-muted); font-size: 12px; line-height: 1.55; }
|
||
|
||
.help-footer { display: flex; align-items: center; justify-content: space-between; min-height: 38px; padding: 0 16px 0 20px; border-top: 1px solid var(--color-border); background: var(--app-dialog-chrome); color: var(--color-ink-faint); font-size: 9.5px; }
|
||
.help-footer span { display: inline-flex; align-items: center; gap: 5px; }
|
||
kbd { display: inline-grid; place-items: center; min-width: 20px; height: 21px; padding: 0 5px; border: 1px solid var(--color-border-input); border-radius: 5px; background: var(--color-surface-raised); color: var(--color-ink-muted); box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.04); font-family: var(--font-mono); font-size: 9px; }
|
||
|
||
@media (max-width: 820px) {
|
||
.help-backdrop { padding: 10px; }
|
||
.help-header { grid-template-columns: minmax(0, 1fr) auto; gap: 10px; }
|
||
.help-title-wrap { display: none; }
|
||
.help-layout { grid-template-columns: 190px minmax(0, 1fr); }
|
||
.help-content { padding-inline: 20px; }
|
||
.help-command-row { grid-template-columns: minmax(0, 1fr) auto; gap: 5px 10px; }
|
||
.help-command-row > span { grid-column: 1; }
|
||
.help-command-row button { grid-column: 2; grid-row: 1 / 3; }
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.help-layout { grid-template-columns: 1fr; }
|
||
.help-nav { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--color-border); }
|
||
.help-nav-label, .help-nav-tip, .help-nav-chevron { display: none; }
|
||
.help-nav > button { flex: 0 0 auto; width: auto; grid-template-columns: 24px auto; }
|
||
.help-content { padding-inline: 15px; }
|
||
.help-section { grid-template-columns: 1fr; }
|
||
.help-section-number { display: none; }
|
||
.help-footer { display: none; }
|
||
}
|
||
</style>
|