diff --git a/src/lib/components/HelpOverlay.svelte b/src/lib/components/HelpOverlay.svelte index 6a300da..170a273 100644 --- a/src/lib/components/HelpOverlay.svelte +++ b/src/lib/components/HelpOverlay.svelte @@ -13,7 +13,9 @@ GitCommitHorizontal, Home, Keyboard, + Library, Lightbulb, + ListChecks, Search, Wrench, X, @@ -465,6 +467,732 @@ }, ]; + // 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 ", description: "Zeigen, welche Ignore-Regel auf eine Datei wirkt" }, + { command: "git rm --cached ", 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 ", description: "Metadaten und Patch eines Commits anzeigen" }, + { command: "git show :", description: "Dateiinhalt aus einem bestimmten Commit ausgeben" }, + { command: "git diff ..", description: "Zwei Zustände direkt vergleichen" }, + { command: "git log --follow -- ", description: "Dateiverlauf über Umbenennungen hinweg verfolgen" }, + ], + }, + ); + + 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 ", description: "Einen Commit auf den aktuellen Branch kopieren" }, + { command: "git cherry-pick --no-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/", 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 ", description: "Nicht gestagte Dateiänderungen verwerfen" }, + { command: "git restore --staged ", 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 ", 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/", 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/", description: "Ziel-Branch wechseln" }, + { command: "git pull --ff-only", description: "Sicherstellen, dass der Ziel-Branch aktuell ist" }, + { command: "git cherry-pick -x ", 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 ", description: "Show which ignore rule applies to a file" }, + { command: "git rm --cached ", 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 ", description: "Show a commit's metadata and patch" }, + { command: "git show :", description: "Print a file from a specific commit" }, + { command: "git diff ..", description: "Compare two states directly" }, + { command: "git log --follow -- ", description: "Follow file history across renames" }, + ], + }, + ); + + 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 ", description: "Copy one commit onto the current branch" }, + { command: "git cherry-pick --no-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/", 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 ", description: "Discard unstaged file changes" }, + { command: "git restore --staged ", 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 ", 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/", 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/", description: "Switch to the target branch" }, + { command: "git pull --ff-only", description: "Ensure the target branch is current" }, + { command: "git cherry-pick -x ", 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.", + }, + ], + }, + ); + let { language = "en", onClose = () => {} }: Props = $props(); const isGerman = $derived(language === "de"); const categories = $derived(isGerman ? deCategories : enCategories); @@ -561,19 +1289,21 @@