Git jenseits der Grundlagen: Workflows, die jede Woche Stunden sparen
Interactive Rebase, Cherry-Pick, Bisect, Worktrees, Reflog-Rettung und die Branching-Strategien, die wirklich funktionieren. Git-Befehle, die ich täglich verwende und die die meisten Entwickler nicht kennen.
Die meisten Entwickler lernen fünf Git-Befehle und hören auf. add, commit, push, pull, merge. Vielleicht checkout und branch, wenn sie abenteuerlustig sind. Damit kommt man durch das erste Jahr. Dann hat dein Branch 47 Commits mit Nachrichten wie „fix" und „wip" und „bitte funktioniere", du hast versehentlich etwas zurückgesetzt, das du nicht hättest zurücksetzen sollen, und du verbringst 40 Minuten auf Stack Overflow damit, einen misslungenen Merge rückgängig zu machen.
Ich benutze Git seit Jahren. Nicht beiläufig — intensiv. Mehrere Branches, mehrere Repos, mehrere Mitarbeiter, den ganzen Tag, jeden Tag. Was folgt, sind die Befehle und Workflows, die ich tatsächlich verwende. Nicht die, die in einem Tutorial gut aussehen. Die, die mir jede einzelne Woche echte Zeit sparen.
Interactive Rebase: Räum deinen Mist auf, bevor ihn jemand sieht#
Dein Branch hat zwölf Commits. Die Hälfte davon ist „fix typo". Einer sagt „vorherigen Commit rückgängig machen". Ein anderer sagt „diesmal wirklich fixen". Du bist dabei, einen PR zu öffnen. Niemand muss diese History sehen.
Interactive Rebase ist, wie du die History auf deinem Branch umschreibst, bevor du ihn teilst. Damit kannst du Commits zusammenquetschen, Nachrichten umformulieren, sie umordnen oder komplett verwerfen.
Der grundlegende Befehl#
git rebase -i HEAD~5Das öffnet einen Editor, der deine letzten 5 Commits zeigt, älteste zuerst:
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsJede Zeile beginnt mit einem Befehl. Ändere pick zu einem dieser:
squash(oders) — Diesen Commit mit dem darüber zusammenführen, Nachrichten kombinierenfixup(oderf) — Wie Squash, aber die Nachricht dieses Commits verwerfenreword(oderr) — Commit behalten aber seine Nachricht änderndrop(oderd) — Diesen Commit komplett löschenedit(odere) — Den Rebase bei diesem Commit pausieren, damit du ihn bearbeiten kannst
Eine echte Aufräum-Session#
Hier ist, was ich tatsächlich mache. Die unordentliche History von oben wird zu:
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsSpeichern und schließen. Jetzt hast du drei saubere Commits statt fünf. Der Typo-Fix wird in den Auth-Commit gefaltet. Der Rate-Limit-Bug-Fix wird in den Rate-Limit-Commit gefaltet. Dein PR-Reviewer sieht einen sauberen, logischen Fortschritt.
Commits umordnen#
Du kannst die Zeilen buchstäblich umordnen. Wenn der Test-Commit vor dem Rate-Limiting-Commit kommen soll, verschiebe einfach die Zeile:
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limitingGit spielt deine Commits in dieser neuen Reihenfolge ab. Wenn es Konflikte gibt, pausiert es und lässt dich sie lösen.
Die Autosquash-Abkürzung#
Wenn du weißt, dass ein Commit ein Fix für einen früheren ist, markiere ihn beim Commit-Zeitpunkt:
git commit --fixup=a1b2c3dDas erstellt einen Commit mit der Nachricht fixup! Add user authentication endpoint. Dann beim Rebase:
git rebase -i --autosquash HEAD~5Git ordnet die Fixup-Commits automatisch direkt unter ihren Zielen ein und markiert sie als fixup. Du musst nur speichern und schließen. Kein manuelles Bearbeiten.
Ich benutze das ständig. Es ist der schnellste Weg, auf einem Branch zu iterieren und dabei die finale History sauber zu halten.
Die goldene Regel#
Rebase niemals Commits, die auf einen geteilten Branch gepusht wurden. Wenn andere Leute Arbeit auf diesen Commits aufgebaut haben, wird das Umschreiben der History echte Probleme verursachen. Rebase deine eigenen Feature-Branches vor dem Mergen. Rebase niemals main.
Wenn du deinen Feature-Branch schon gepusht hast und rebasing brauchst:
git push --force-with-leaseDas --force-with-lease-Flag ist sicherer als --force. Es weigert sich zu pushen, wenn jemand anderes seit deinem letzten Fetch auf denselben Branch gepusht hat. Es verhindert nicht alle Probleme, aber es fängt das häufigste ab.
Cherry-Pick: Chirurgische Commit-Transfers#
Cherry-Pick nimmt einen bestimmten Commit von einem Branch und wendet ihn auf einen anderen an. Kein Merge. Kein Rebase. Nur ein Commit, sauber angewendet.
Wann ich das tatsächlich verwende#
Das häufigste Szenario: Ich habe einen Bug auf meinem Feature-Branch gefixt, aber der Fix muss auch sofort auf main oder einen Release-Branch. Ich will nicht meinen gesamten Feature-Branch mergen. Ich will nur diesen einen Fix.
# Den Commit-Hash des Fixes finden
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
# Zu main wechseln und cherry-picken
git checkout main
git cherry-pick a1b2c3dFertig. Der Fix ist auf main als neuer Commit mit denselben Änderungen.
Cherry-Pick ohne Committen#
Manchmal willst du die Änderungen anwenden, aber noch nicht committen. Vielleicht willst du mehrere Cherry-Picks zu einem Commit kombinieren oder die Änderungen leicht modifizieren:
git cherry-pick --no-commit a1b2c3dDie Änderungen sind gestaged aber nicht committed. Du kannst sie modifizieren, weitere Änderungen hinzufügen und dann committen, wenn du bereit bist.
Bereichs-Cherry-Pick#
Brauchst du mehrere aufeinanderfolgende Commits? Verwende Bereichssyntax:
git cherry-pick a1b2c3d..f6g7h8iDas cherry-pickt alles nach a1b2c3d bis einschließlich f6g7h8i. Beachte, dass a1b2c3d selbst ausgeschlossen ist. Wenn du ihn einschließen willst:
git cherry-pick a1b2c3d^..f6g7h8iKonflikte behandeln#
Cherry-Pick-Konflikte funktionieren wie Merge-Konflikte. Wenn einer auftritt:
# Git sagt dir, dass es einen Konflikt gibt
# Behebe die konfliktierenden Dateien, dann:
git add .
git cherry-pick --continueOder wenn du es dir anders überlegst:
git cherry-pick --abortEine Sache, auf die du achten solltest: Cherry-gepickte Commits erstellen neue Commit-Hashes. Wenn du später den Original-Branch mergst, ist Git normalerweise schlau genug, die Duplikation zu handhaben. Aber wenn du aggressiv zwischen Branches cherry-pickst, die irgendwann zusammengeführt werden, könntest du unerwartete Konflikte sehen. Verwende es chirurgisch, nicht als Merge-Strategie.
Git Bisect: Binäre Suche nach Bugs#
Etwas ist kaputt. Du weißt, es funktionierte vor zwei Wochen. Es gab 200 Commits seitdem. Welcher hat es kaputt gemacht?
Du könntest jeden Commit manuell prüfen. Oder du könntest git bisect verwenden, das binäre Suche nutzt, um den exakten brechenden Commit in log2(n) Schritten zu finden. Bei 200 Commits sind das etwa 7-8 Prüfungen statt 200.
Der manuelle Weg#
# Bisecting starten
git bisect start
# Aktuellen Commit als schlecht markieren (der Bug existiert hier)
git bisect bad
# Einen bekannten guten Commit markieren (der Bug existierte hier nicht)
git bisect good v2.1.0Git checkt einen Commit auf halbem Weg zwischen gut und schlecht aus. Teste ihn. Dann:
# Wenn der Bug bei diesem Commit existiert:
git bisect bad
# Wenn der Bug bei diesem Commit nicht existiert:
git bisect goodGit halbiert den Bereich jedes Mal. Nach 7-8 Schritten sagt es dir:
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
Refactor session handling to use async middlewareJetzt weißt du genau, welcher Commit den Bug eingeführt hat. Wenn du fertig bist:
git bisect resetDas bringt dich zurück dahin, wo du angefangen hast.
Der automatisierte Weg (Das ist die wahre Stärke)#
Wenn du einen Test hast, der den Bug erkennen kann, kannst du das Ganze automatisieren:
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"Git wird automatisch Commits auschecken, den Test ausführen und sie basierend auf dem Exit-Code als gut oder schlecht markieren. Null bedeutet gut, nicht-null bedeutet schlecht. Geh weg, komm zurück, und es sagt dir den exakten Commit.
Du kannst jedes Script verwenden:
git bisect run ./test-regression.shWobei test-regression.sh ist:
#!/bin/bash
npm run build 2>/dev/null || exit 125 # 125 bedeutet "diesen Commit überspringen"
npm test -- --grep "session" || exit 1 # 1 bedeutet "schlecht"
exit 0 # 0 bedeutet "gut"Exit-Code 125 ist besonders — er sagt Bisect, diesen Commit zu überspringen (nützlich, wenn ein Commit nicht kompiliert). Das ist eines dieser Features, das nischenhaft erscheint, bis du es brauchst, und dann rettet es dir einen ganzen Nachmittag.
Eine echte Bisect-Session#
So sieht es in der Praxis aus:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
$ npm test -- --grep "login flow"
# Tests schlagen fehl
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
$ npm test -- --grep "login flow"
# Tests bestanden
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
# Nach ~7 Iterationen:
abc1234def5678ghi is the first bad commitSieben Schritte, um eine Nadel im Heuhaufen von 200 Commits zu finden.
Git Worktrees: Mehrere Branches, null Stashing#
Das ist das am meisten unterbewertete Git-Feature. Ich bin überzeugt, dass die meisten Entwickler nicht wissen, dass es existiert.
Das Problem: Du steckst tief in einem Feature-Branch. Dateien sind überall geändert. Dann sagt jemand „Kannst du dir kurz diesen Produktions-Bug anschauen?" Du hast drei Optionen:
- Alles stashen, Branches wechseln, fixen, zurückwechseln, Stash poppen. Hoffen, dass nichts schiefgeht.
- Deine halbfertige Arbeit mit einer „wip"-Nachricht committen. Hässlich aber funktional.
- Das Repo nochmal in ein anderes Verzeichnis klonen.
Oder Option 4: Worktrees.
Was ein Worktree ist#
Ein Worktree ist ein zweites (oder drittes, oder viertes) Arbeitsverzeichnis, das mit demselben Repository verknüpft ist. Jeder Worktree hat seinen eigenen ausgecheckten Branch, seine eigenen Arbeitsdateien, seinen eigenen Index. Aber sie teilen dieselben .git-Daten, du duplizierst also nicht das gesamte Repo.
Einen Worktree hinzufügen#
# Du bist auf feature/user-auth, tief in der Arbeit
# Musst einen Bug auf main fixen:
git worktree add ../hotfix-session mainDas erstellt ein neues Verzeichnis ../hotfix-session mit ausgechecktem main. Dein aktuelles Verzeichnis bleibt exakt so wie es war. Nichts gestasht, nichts committed, nichts gestört.
cd ../hotfix-session
# Den Bug fixen
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Weiter an deinem Feature arbeiten, als wäre nichts passiertEinen neuen Branch in einem Worktree erstellen#
git worktree add ../hotfix-nav -b hotfix/nav-crash mainDas erstellt den Worktree UND erstellt einen neuen Branch hotfix/nav-crash basierend auf main.
Worktrees verwalten#
# Alle Worktrees auflisten
git worktree list
# /home/dev/my-project abc1234 [feature/user-auth]
# /home/dev/hotfix-session def5678 [main]
# Einen Worktree entfernen wenn fertig
git worktree remove ../hotfix-session
# Wenn das Verzeichnis schon gelöscht wurde:
git worktree pruneWarum das besser ist als Stashing#
Stashing ist gut für schnelle Kontextwechsel. Aber Worktrees sind besser für alles, das länger als fünf Minuten dauert:
- Deine IDE bleibt auf deinem Feature-Branch offen. Kein Neuindizieren, kein Verlust deiner Scrollposition.
- Du kannst Tests in beiden Verzeichnissen gleichzeitig ausführen.
- Kein Risiko von Stash-Konflikten oder Vergessen, was du gestasht hast.
- Du kannst einen lang laufenden Dev-Server in einem Worktree haben und einen sauberen Build in einem anderen.
Ich halte typischerweise zwei oder drei Worktrees aktiv: meinen Haupt-Feature-Branch, einen main-Worktree für schnelle Checks, und manchmal einen Review-Worktree, wo ich den PR von jemand anderem auschecke.
Der eine Haken#
Du kannst nicht denselben Branch in zwei Worktrees ausgecheckt haben. Das ist beabsichtigt — es verhindert, dass du widersprüchliche Änderungen am selben Branch an zwei Stellen machst. Wenn du es versuchst, wird Git es verweigern.
Reflog: Der Rückgängig-Button für alles#
Du hast einen Hard Reset gemacht und Commits verloren. Du hast einen Branch gelöscht. Du hast rebased und alles ging fürchterlich schief. Du denkst, deine Arbeit ist weg.
Ist sie nicht. Git löscht fast nie tatsächlich etwas. Das Reflog ist dein Sicherheitsnetz.
Was das Reflog ist#
Jedes Mal, wenn HEAD sich bewegt — jeder Commit, Checkout, Rebase, Reset, Merge — zeichnet Git es im Reflog auf. Es ist ein Protokoll davon, wo dein HEAD war, der Reihe nach.
git reflogAusgabe:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to mainJeder Eintrag hat einen Index (HEAD@{0}, HEAD@{1}, etc.) und eine Beschreibung, was passiert ist.
Wiederherstellen nach einem Hard Reset#
Du hast versehentlich git reset --hard HEAD~3 ausgeführt und drei Commits verloren. Sie sind direkt im Reflog:
# Sieh, was du verloren hast
git reflog
# Der Commit vor dem Reset ist HEAD@{1}
git reset --hard f4e5d6cAlle drei Commits sind zurück. Krise abgewendet.
Einen gelöschten Branch wiederherstellen#
Du hast einen Branch gelöscht, der ungemergte Arbeit hatte:
git branch -D feature/experimental
# Oh nein, da steckten zwei Wochen Arbeit drinDie Commits existieren noch. Finde sie:
git reflog | grep "feature/experimental"
# Oder schau einfach durch das Reflog nach dem letzten Commit auf diesem Branch
# Gefunden. Branch an diesem Commit wiederherstellen:
git branch feature/experimental a1b2c3dDer Branch ist zurück, mit allen seinen Commits.
Wiederherstellen nach einem schlechten Rebase#
Du hast rebased und alles ging schief. Konflikte überall, falsche Commits, Chaos:
# Das Reflog zeigt, wo du vor dem Rebase warst
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
# Zurück zum Zustand vor dem Rebase
git reset --hard b7a8c9dDu bist genau da, wo du vor dem Rebase warst. Als wäre es nie passiert.
Das 30-Tage-Sicherheitsnetz#
Standardmäßig behält Git Reflog-Einträge für 30 Tage (90 Tage für erreichbare Commits). Danach können sie vom Garbage Collector entfernt werden. Du hast also einen Monat Zeit, um zu merken, dass du einen Fehler gemacht hast. In der Praxis ist das mehr als genug.
Du kannst den Ablauf prüfen:
git config gc.reflogExpire
# Standard: 90.days.ago (für erreichbare)
git config gc.reflogExpireUnreachable
# Standard: 30.days.ago (für nicht erreichbare)Wenn du paranoid bist, erhöhe es:
git config --global gc.reflogExpireUnreachable "180.days.ago"Eine persönliche Regel#
Vor jeder destruktiven Operation — Hard Reset, Force Push, Branch-Löschung — führe ich zuerst git log --oneline -10 aus. Ich merke mir mental den aktuellen HEAD. Es dauert zwei Sekunden und hat mich mehr als einmal vor einer Panik bewahrt, die ich nicht hätte haben müssen.
Stash richtig verwenden: Es ist nicht nur git stash#
Die meisten Leute benutzen Stash so:
git stash
# etwas tun
git stash popDas funktioniert, aber es ist das Äquivalent davon, alles in eine Kiste mit der Aufschrift „Zeug" zu werfen. Wenn du drei Stashes hast, hast du keine Ahnung, welcher welcher ist.
Stashes benennen#
git stash push -m "WIP: user auth form validation"Jetzt, wenn du Stashes auflistest:
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooksDu kannst genau sehen, was jeder Stash enthält.
Ungetrackte Dateien einschließen#
Standardmäßig stasht git stash nur getrackte Dateien. Neue Dateien, die du noch nicht hinzugefügt hast, bleiben zurück:
# Alles stashen, einschließlich neuer Dateien
git stash push --include-untracked -m "WIP: new auth components"
# Oder sogar ignorierte Dateien einschließen (selten nötig)
git stash push --all -m "Full workspace snapshot"Ich verwende --include-untracked fast jedes Mal. Neue Dateien zurückzulassen, wenn du Branches wechselst, verursacht Verwirrung.
Partielles Stashing#
Das ist das, was die meisten Leute nicht wissen. Du kannst bestimmte Dateien stashen:
# Nur bestimmte Dateien stashen
git stash push -m "Just the auth changes" src/auth/ src/middleware.tsOder verwende den Patch-Modus, um bestimmte Hunks innerhalb von Dateien zu stashen:
git stash push --patch -m "Partial: only the validation logic"Git geht jede Änderung interaktiv durch und fragt, ob du sie stashen willst. y für ja, n für nein, s um den Hunk in kleinere Stücke aufzuteilen.
Apply vs Pop#
# Pop: anwenden und aus der Stash-Liste entfernen
git stash pop stash@{2}
# Apply: anwenden aber in der Stash-Liste behalten
git stash apply stash@{2}Ich verwende apply, wenn ich nicht sicher bin, ob der Stash sauber angewendet wird. Wenn es einen Konflikt gibt, bleibt der Stash erhalten. Bei pop, wenn es einen Konflikt gibt, bleibt der Stash auch in der Liste (viele Leute wissen das nicht), aber ich bevorzuge die explizite Absicht von apply.
Stash-Inhalte anzeigen#
# Sehen, welche Dateien in einem Stash geändert wurden
git stash show stash@{0}
# Den vollständigen Diff sehen
git stash show -p stash@{0}Einen Branch aus einem Stash erstellen#
Wenn dein Stash zu etwas Substanziellerem geworden ist:
git stash branch feature/auth-validation stash@{0}Das erstellt einen neuen Branch vom Commit, bei dem du ursprünglich gestasht hast, wendet den Stash an und verwirft ihn. Sauber.
Branching-Strategien: Welche tatsächlich funktioniert#
Es gibt drei Mainstream-Branching-Strategien. Jede hat einen Kontext, in dem sie glänzt, und Kontexte, in denen sie Schmerzen verursacht.
Gitflow#
Der Klassiker. main, develop, feature/*, release/*, hotfix/*. Erstellt von Vincent Driessen im Jahr 2010.
# Feature-Branch
git checkout -b feature/user-auth develop
# ... arbeiten ...
git checkout develop
git merge --no-ff feature/user-auth
# Release-Branch
git checkout -b release/2.1.0 develop
# ... letzte Fixes ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
# Hotfix
git checkout -b hotfix/session-fix main
# ... fix ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fixWann es funktioniert: Mobile Apps, Desktop-Software, alles mit benannten versionierten Releases und gleichzeitig unterstützten mehreren Versionen. Wenn du v2.1 und v3.0 auslieferst und beide patchen musst, handhabt Gitflow das.
Wann nicht: Webanwendungen mit Continuous Deployment. Wenn du 5-mal am Tag in die Produktion deployest, ist die Zeremonie von Release-Branches und Develop-Branches reiner Overhead. Die meisten Web-Teams, die Gitflow übernehmen, enden mit einem Develop-Branch, der dauerhaft kaputt ist, und Release-Branches, die niemand versteht.
GitHub Flow#
Einfach. Du hast main. Du erstellst Feature-Branches. Du öffnest PRs. Du mergst zu main. Du deployest main.
git checkout -b feature/user-auth main
# ... arbeiten ...
git push origin feature/user-auth
# PR öffnen, reviewen lassen, mergen
# main ist immer deploybarWann es funktioniert: Kleine bis mittlere Teams, die Web-Apps ausliefern. Continuous Deployment. Wenn main immer deployed wird, ist das alles, was du brauchst. Es ist das, was diese Seite verwendet.
Wann nicht: Wenn du mehrere Release-Versionen pflegen musst, oder wenn du einen langen QA-Zyklus vor dem Deployment hast. GitHub Flow setzt voraus, dass main schnell in die Produktion geht.
Trunk-Based Development#
Alle committen direkt zu main (dem „Trunk") oder über sehr kurzlebige Branches (weniger als ein Tag). Keine lang laufenden Feature-Branches.
# Kurzlebiger Branch (am selben Tag gemergt)
git checkout -b fix/auth-token main
# ... kleine, fokussierte Änderung ...
git push origin fix/auth-token
# PR reviewt und innerhalb von Stunden gemergt, nicht TagenWann es funktioniert: Hochperformante Teams mit gutem CI/CD, umfassenden Testsuiten und Feature Flags. Google, Meta und die meisten großen Tech-Unternehmen verwenden Trunk-Based Development. Es erzwingt kleine, inkrementelle Änderungen und eliminiert die Merge-Hölle.
Wann nicht: Teams ohne gute Testabdeckung oder CI. Wenn das Mergen zum Trunk bedeutet, ungetesteten Code zu deployen, wirst du die Produktion ständig kaputt machen. Du brauchst auch Feature Flags für alles, das länger als einen Tag zum Bauen braucht:
# Feature Flag im Code
if (featureFlags.isEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}Meine Empfehlung#
Für die meisten Webentwicklungsteams: Starte mit GitHub Flow. Es ist einfach, es funktioniert, und es erfordert kein Tooling über das hinaus, was GitHub/GitLab bereits bietet.
Wenn dein Team über 15-20 Entwickler wächst und du mehrmals am Tag deployest, schau dir Trunk-Based Development mit Feature Flags an. Die Investition in Feature-Flag-Infrastruktur zahlt sich aus durch weniger Merge-Konflikte und schnellere Iteration.
Wenn du versionierte Software auslieferst (Mobile Apps, CLI-Tools, Bibliotheken): Gitflow oder eine vereinfachte Version davon. Du brauchst tatsächlich diese Release-Branches.
Wähle keine Strategie, weil ein Blog-Post gesagt hat, sie wäre die beste. Wähle die, die zu dem passt, wie du tatsächlich auslieferst.
Git Hooks: Automatisiere die Dinge, die du ständig vergisst#
Git Hooks sind Scripts, die automatisch an bestimmten Punkten im Git-Workflow ausgeführt werden. Sie sind lokal auf deiner Maschine (nicht zum Remote gepusht), was bedeutet, du brauchst einen Weg, sie mit deinem Team zu teilen.
Die Hooks, die tatsächlich wichtig sind#
pre-commit — Läuft vor jedem Commit. Verwende es für Linting und Formatierung:
#!/bin/bash
# .git/hooks/pre-commit
# ESLint nur auf gestagten Dateien ausführen
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$STAGED_FILES" ]; then
echo "Running ESLint on staged files..."
npx eslint $STAGED_FILES --quiet
if [ $? -ne 0 ]; then
echo "ESLint failed. Fix errors before committing."
exit 1
fi
fi
# Prettier auf gestagten Dateien ausführen
if [ -n "$STAGED_FILES" ]; then
echo "Running Prettier..."
npx prettier --check $STAGED_FILES
if [ $? -ne 0 ]; then
echo "Prettier check failed. Run 'npx prettier --write' first."
exit 1
fi
ficommit-msg — Validiert das Commit-Message-Format. Perfekt zum Erzwingen von Conventional Commits:
#!/bin/bash
# .git/hooks/commit-msg
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "Invalid commit message format."
echo "Expected: type(scope): description"
echo "Example: feat(auth): add session refresh endpoint"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
exit 1
fipre-push — Läuft vor dem Pushen. Verwende es für Tests:
#!/bin/bash
# .git/hooks/pre-push
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiHusky vs Native Hooks#
Native Hooks leben in .git/hooks/. Das Problem: Das .git-Verzeichnis wird nicht von Git getrackt, also kannst du Hooks nicht über das Repo teilen. Jeder muss sie manuell einrichten.
Husky löst das. Es speichert Hook-Konfigurationen im Repo und richtet .git/hooks automatisch bei npm install ein:
npx husky initDas erstellt ein .husky/-Verzeichnis. Füge Hooks als Dateien hinzu:
# .husky/pre-commit
npx lint-stagedKombiniert mit lint-staged bekommst du schnelle, gezielte Pre-Commit-Checks:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.css": ["prettier --write"],
"*.json": ["prettier --write"]
}
}Das führt ESLint und Prettier nur auf den Dateien aus, die du tatsächlich commitest. Nicht die gesamte Codebase. Schnell.
Wann Hooks übersprungen werden#
Manchmal musst du ohne laufende Hooks committen. Notfall-Hotfixes, Work-in-Progress-Commits auf deinem eigenen Branch:
git commit --no-verify -m "WIP: debugging production issue"Verwende das sparsam. Wenn du dich dabei ertappst, Hooks regelmäßig zu überspringen, sind deine Hooks wahrscheinlich zu langsam oder zu streng.
Aliases, die Zeit sparen#
Meine .gitconfig hat sich über Jahre entwickelt. Das sind die Aliases, die überlebt haben — die, die ich tatsächlich täglich verwende, nicht die, die ich hinzugefügt habe, weil sie clever aussahen.
Schönes Log#
Das Standard-git log ist ausführlich. Das gibt dir eine saubere, farbige, graphenbasierte Ansicht:
git config --global alias.lg "log --oneline --graph --all --decorate"Verwendung:
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboardIch führe das 20-mal am Tag aus. Es ist der schnellste Weg, den Zustand deines Repositories zu verstehen.
Letzten Commit rückgängig machen#
Die Änderungen behalten, nur den Commit rückgängig machen:
git config --global alias.undo "reset HEAD~1 --mixed"Verwendung:
git undo
# Commit ist weg, aber alle Änderungen sind noch in deinem ArbeitsverzeichnisIch verwende das, wenn ich zu früh committe, eine Datei vergesse oder die Änderungen umstrukturieren will.
Alles unstagen#
git config --global alias.unstage "reset HEAD --"Verwendung:
git unstage src/auth/session.ts
# Datei ist ungestaged aber Änderungen sind erhaltenAlle Aliases auflisten#
Weil du vergessen wirst, was du eingerichtet hast:
git config --global alias.aliases "config --get-regexp ^alias\\."Weitere Aliases, die ich verwende#
# Zeigen, was du gleich committest
git config --global alias.staged "diff --staged"
# Kurzer Status
git config --global alias.st "status -sb"
# Amend ohne Nachricht zu ändern
git config --global alias.amend "commit --amend --no-edit"
# Den letzten Commit zeigen
git config --global alias.last "log -1 HEAD --stat"
# Pull mit Rebase statt Merge
git config --global alias.up "pull --rebase --autostash"
# Branches löschen, die zu main gemergt wurden
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"Der up-Alias ist besonders gut. pull --rebase hält deine History linear, statt Merge-Commits für jeden Pull zu erstellen. --autostash stasht und restored deine Änderungen automatisch, wenn du schmutzige Dateien hast. Es ist das, was Pull standardmäßig hätte sein sollen.
Der cleanup-Alias löscht lokale Branches, die zu main gemergt wurden. Mit der Zeit sammelst du Dutzende veraltete Branches an. Führe das wöchentlich aus.
Die Befehle, die ich täglich verwende#
Das sind keine Aliases oder erweiterte Features. Es sind einfach Befehle, die ich ständig ausführe und die viele Entwickler nicht zu kennen scheinen.
Der ultimative Log-Befehl#
git log --oneline --graph --allDas zeigt jeden Branch, jeden Merge, die gesamte Topologie deines Repos in einer kompakten Ansicht. Es ist das Erste, was ich ausführe, wenn ich Änderungen pulle. Es beantwortet „Was passiert gerade in diesem Repo?"
Gestagte Änderungen diffen#
git diff --stagedDas zeigt, was gleich committed wird. Nicht was in deinem Arbeitsverzeichnis geändert ist — was tatsächlich gestaged ist. Ich führe das vor jedem Commit aus. Immer. Es fängt versehentliche Einschlüsse, Debug-Statements, console.logs die nicht da sein sollten.
# Nicht gestagte Änderungen sehen
git diff
# Gestagte Änderungen sehen
git diff --staged
# Beides sehen
git diff HEADEinen bestimmten Commit anzeigen#
git show a1b2c3dZeigt den vollständigen Diff eines einzelnen Commits. Nützlich beim Reviewen der History, zum Verstehen was ein Commit tatsächlich geändert hat.
# Nur die geänderten Dateien zeigen
git show --stat a1b2c3d
# Eine bestimmte Datei bei einem bestimmten Commit zeigen
git show a1b2c3d:src/auth/session.tsDas Letzte ist unglaublich nützlich. Du kannst jede Datei an jedem Punkt der History anzeigen, ohne diesen Commit auszuchecken.
Blame mit Zeilenbereichen#
git blame -L 42,60 src/auth/session.tsZeigt, wer die Zeilen 42-60 zuletzt geändert hat. Nützlicher als die gesamte Datei zu blamen, was normalerweise überwältigend ist.
# Auch die Commit-Nachricht zeigen, nicht nur den Hash
git blame -L 42,60 --show-name src/auth/session.ts
# Whitespace-Änderungen ignorieren (sehr nützlich)
git blame -w -L 42,60 src/auth/session.ts
# Den Commit vor dem ge-blameten zeigen (tiefer graben)
git log --follow -p -- src/auth/session.tsDas -w-Flag ist wichtig. Ohne es wird Blame Zeilen demjenigen zuordnen, der die Datei zuletzt reformatiert hat, was selten die Person ist, die du suchst.
Einen String über die gesamte History finden#
# Finden, wann eine Funktion hinzugefügt oder entfernt wurde
git log -S "validateSession" --oneline
# Finden, wann ein Regex-Pattern auftauchte
git log -G "session.*timeout" --oneline-S (die „Pickaxe") findet Commits, bei denen sich die Anzahl der Vorkommnisse eines Strings geändert hat. -G findet Commits, bei denen der Diff einem Regex entspricht. Beide sind mächtig für Archäologie — herauszufinden, wann etwas eingeführt oder entfernt wurde.
Zeigen, was sich zwischen zwei Punkten geändert hat#
# Was sich zwischen zwei Branches geändert hat
git diff main..feature/auth
# Was sich auf diesem Branch geändert hat, seit er von main abzweigte
git diff main...feature/auth
# Nur die geänderten Dateien auflisten
git diff main...feature/auth --name-only
# Stat-Ansicht (Dateien + Einfügungen/Löschungen)
git diff main...feature/auth --statZwei Punkte vs drei Punkte ist wichtig. Zwei Punkte zeigt den Unterschied zwischen den Spitzen beider Branches. Drei Punkte zeigt, was sich auf der rechten Seite geändert hat, seit sie von der linken Seite abzweigte. Drei Punkte ist normalerweise das, was du willst, wenn du einen Feature-Branch reviewst.
Ungetrackte Dateien aufräumen#
# Sehen, was gelöscht werden würde (Trockenlauf)
git clean -n
# Ungetrackte Dateien löschen
git clean -f
# Ungetrackte Dateien und Verzeichnisse löschen
git clean -fd
# Ungetrackte und ignorierte Dateien löschen (nukleare Option)
git clean -fdxFühre immer zuerst mit -n aus. git clean -fdx wird deine node_modules, .env, Build-Artefakte löschen — alles, was nicht von Git getrackt wird. Nützlich für einen wirklich frischen Start, aber destruktiv.
Eine einzelne Datei von einem anderen Branch wiederherstellen#
# Die main-Branch-Version einer Datei holen, ohne Branches zu wechseln
git restore --source main -- src/config/database.tsOder von einem bestimmten Commit:
git restore --source a1b2c3d -- src/config/database.tsDas ist sauberer als die alte git checkout main -- path/to/file-Syntax, und es beeinflusst HEAD nicht.
Alles zusammen: Ein echter Workflow#
So sieht ein typischer Tag mit diesen Tools aus:
# Morgens: prüfen, was passiert
git lg
git fetch --all
# Ein Feature starten
git checkout -b feature/session-refresh main
# Arbeiten, inkrementell committen
git add -p # Bestimmte Hunks stagen, nicht ganze Dateien
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
# Unterbrochen: einen Produktions-Bug fixen
git worktree add ../hotfix main
cd ../hotfix
# ... fix, commit, push, PR gemergt ...
cd ../my-project
git worktree remove ../hotfix
# Zurück zur Feature-Arbeit
git commit -m "Fix edge case in token validation"
# Bereit für PR: History aufräumen
git rebase -i main
# Fix-Commits squashen, für Klarheit umformulieren
# Pushen und PR öffnen
git push -u origin feature/session-refresh
# Etwas im Staging kaputt? Herausfinden, welcher Commit:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
# Ups, ich habe das Falsche hard-resettet
git reflog
git reset --hard HEAD@{2}Jeder dieser Befehle dauert Sekunden. Zusammen sparen sie Stunden. Nicht hypothetische Stunden — echte Stunden, jede Woche, die ich sonst damit verbringen würde, Git-Chaos zu entwirren, manuell nach Bugs zu suchen oder Arbeit durch Kontextwechsel zu verlieren.
Git ist ein Werkzeug, das Tiefe belohnt. Die Grundlagen bringen dich durch den Tag. Aber die Befehle in diesem Beitrag sind das, was „Ich benutze Git" von „Git macht mich tatsächlich schneller" trennt. Lerne sie inkrementell. Nimm dir diese Woche eine neue Technik vor und verwende sie, bis sie in Fleisch und Blut übergegangen ist. Dann nimm die nächste.
Dein zukünftiges Ich, das um 23 Uhr auf einen Produktions-Bug starrt, wird dir dankbar sein, dass du git bisect kennst.