CLI-Adventskalender, Tag 18: git

CLI-Adventskalender: git

Avatar von Eric

In der 18. Ausgabe unseres Adventskalenders beschäftigen wir uns mit git.

Vielleicht ist es merkwürdig ein Tool zu erwähnen, das die meisten Entwickler jeden Tag benutzen. Git ist vielleicht das mächtigste Werkzeug, über das ich im ganzen Kalender rede. Es hat viele versteckte Tiefen, die vielen Entwicklern nicht klar sind und es bietet Nicht-Entwicklern Anwendungsmöglichkeiten, die ihresgleichen suchen.

Skalierung.

Jede Software ist für eine bestimmte Skalierungsstufe optimiert. Manche Software möchte es einzelnen Nutzern ergonomisch erleichtern, ihre einzelnen Dateien zu bearbeiten. Andere sind dafür vorgesehen, zehntausende Dateien über viele Teams hinweg zu versionieren. Je höher die Skalierung ist, desto unbequemer fühlt sich das Tool an. Ein normales Backuptool ist schließlich im Hintergrund und sichert Sachen, während ein Tool wie git sehr präzise Aussagen darüber möchte, was genau gespeichert werden soll, in welchem Commit, auf welchem Branch und zu welchem Remote es soll. Ganz zu schweigen davon, wie es mit Konflikten umgehen soll und der Möglichkeit, die Geschichte jederzeit umzuschreiben.

Commits in ordentlich

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add *
git commit -a -m "Mehr Code bla"
git push
git add * git commit -a -m "Mehr Code bla" git push
git add *
git commit -a -m "Mehr Code bla"
git push
XKCD zum Thema git.
Quelle: Randall Munroe – xkcd.com/1296

Anfänger benutzen git gerne als großen Save-Button und haben auf ihren eigenen Projekten einen main-Branch, der einen Stapel undurchsichtiger Commits enthält.

Oder vielleicht war nur ich das.

Ich habe Git damals als großen Speichern-Knopf benutzt. Je mehr man mit anderen Leuten zusammenarbeitet, desto ordentlicher möchte man irgendwann sein. Des weiteren haben „saubere Commits“ die sich um ein spezifisches Thema drehen viele andere Vorteile. Mehr dazu unten.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add -p src/MasterManagerContainerManagerContainer.java
git add -p src/MasterManagerContainerManagerContainer.java
git add -p src/MasterManagerContainerManagerContainer.java

Erlaubt einzelne Änderungen innerhalb von Dateien zu stagen, um so saubere Commits zu schnüren. Das geht auch mit

git stash --patch
git stash --patch, um einen selektiv sauberen Stand hinzubekommen.

Goodies von Mayflower

Keine Sorge – Hilfe ist nah! Melde Dich unverbindlich bei uns und wir schauen uns gemeinsam an, ob und wie wir Dich unterstützen können.

Moment … Staging? Was ist das?

Schnelle Git-Grundlagen

Die bittere Pille theoretischer Grundlagen müssen wir schnell schlucken, damit die anderen Befehle irgendeinen Sinn ergeben.

Eine Datei in einem Git-Repository hat grundsätzlich einen von vier Zuständen:

  • Untracked: Git ist aktuell noch nicht für diese Datei zuständig. Mit einem Eintrag in der .gitignore kann man sie rausfiltern.
  • Tracked/Unmodified: Datei wird von Git verwaltet, wurde aber nicht geändert.
  • Modified: Ungespeicherte Änderungen. Kann auch Deleted oder Moved sein. Oder Conflicted, wenn Änderungen aus zwei verschiedenen Branches in ihr kollidieren.
  • Staged: Ergebnis von
    git add
    git add. Die Änderungen in dieser Datei werden in den nächsten Commit gepackt. 

Ein Git-Repository hat ein Verzeichnis namens

.git
.git, in dem die ganze Magic passiert. Unter anderem liegen dort:

  • Objects: In
    .git/objects
    .git/objects haben einen Hash und sind Blobs (aus denen sich der Inhalt von Dateien zusammensetzt), Commits oder Trees.
  • References: In
    .git/ref
    .git/ref. In
    .git/ref/heads
    .git/ref/heads liegen die Pointer auf den letzten Commits eines jeden lokalen Branches. In
    .git/ref/remotes
    .git/ref/remotes dasselbe für Remote Branches. Tags liegen in
    .git/ref/tags
    .git/ref/tags.
  • Index: Eine Binärdatei, die sagt, was in den nächsten Commit soll. Unsere „Staging Area“, wenn man so will.
  • Configs: Diverse Einstellungen in
    .git/config
    .git/config.
  • Logs: In
    .git/logs
    .git/logs sind die letzten Änderungen verzeichnet.

Ok, ok. Warum haben wir uns das jetzt alles angeschaut? Weil wir mit diesem Wissen allen möglichen coolen Kram machen können.

Git-Objekte

Alle Elemente eines Branches sind lediglich Objekte, die mit ihrem Hash referenziert sind. Deswegen kann ich mir mit

git cherry-pick 50a5cbda3c
git cherry-pick 50a5cbda3c einen bestimmten Commit auf meinen Branch holen. Und deswegen sollte man saubere Commits machen (ich weiß, Marco. Es tut mir leid.).

Mit 

git show 50a5cbda3c
git show 50a5cbda3c kann man gucken, was drin ist.

Obgleich git einen Garbage Collector hat, triggert der nicht sofort, wenn Objects unverlinkt ist. Ich kann also mit

git reflog
git reflog hunderte von Commits anschauen. In der Reihenfolge, in denen sie zuletzt der aktuelle Commit (HEAD) gewesen sind oder im Laufe eines Rebases angefasst wurden. Sehr, sehr nützlich, wenn man nach einigem Foo (technischer Fachausdruck, Oberbegriff für eine Reihe von Fehlerzuständen und Konfigurationsarbeit) plötzlich einen Commit vermisst. Neunzig Tage lang ist er lediglich einen Cherry-Pick weit weg.

Mach meine Ansicht mal schön

Wie jedes Kommandozeilentool möchte ich bei Git auch erst einmal herausfinden, was los ist:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git status
git status
git status

… und wo wir sind und wie wir da hin gekommen sind:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log
git log
git log

Natürlich kann man diese Ansicht auch noch anpassen (und mit einem Alias zum Default machen):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git status -s # Kürzer
git status -vv # Länger
git status -s # Kürzer git status -vv # Länger
git status -s # Kürzer
git status -vv # Länger

Und noch mehr Optionen bietet

git log
git log:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Fancy
git log --graph --all --decorate --oneline
# So viele Farben
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
# Relatives Datum
git log --pretty=format:"%h - %an, %ar : %s" --decorate
# Änderungen an Dateien
git log --stat
# Das Format kann man wirklich hart customized
git log --pretty=format:"%h %s [%an, %cr]"
# Email-Format!
git log --pretty=email --boundary
# Nur die Merges
git log --merges
# Fancy git log --graph --all --decorate --oneline # So viele Farben git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit # Relatives Datum git log --pretty=format:"%h - %an, %ar : %s" --decorate # Änderungen an Dateien git log --stat # Das Format kann man wirklich hart customized git log --pretty=format:"%h %s [%an, %cr]" # Email-Format! git log --pretty=email --boundary # Nur die Merges git log --merges
# Fancy
git log --graph --all --decorate --oneline
# So viele Farben
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
# Relatives Datum
git log --pretty=format:"%h - %an, %ar : %s" --decorate
# Änderungen an Dateien
git log --stat
# Das Format kann man wirklich hart customized
git log --pretty=format:"%h %s [%an, %cr]"
# Email-Format!
git log --pretty=email --boundary
# Nur die Merges
git log --merges

Wie gesagt, dass man eine Formatzeile dieser Art ad-hoc formuliert sollte eher selten vorkommen. Stattdessen möchte man wahrscheinlich ein Alias definieren.

Das kann man nicht nur in der

.bashrc
.bashrc tun, sondern auch in git selbst:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git config --global alias.farben 'log --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit'
git farben
git config --global alias.farben 'log --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit' git farben
git config --global alias.farben 'log --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit'
git farben

Wer hat das verbrochen?

Git ist gnadenlos gründlich darin zu tracken, wer wann was verändert hat. Je nach Firmenkultur kann das auch was Positives sein.

Mit

git shortlog
git shortlog kann man alle Commits eines Repos geclustert nach AutorInnen sehen.

Der klassische „Beender“ eines freundlichen Arbeitsklimas ist

git blame <file>
git blame <file>.

Ansonsten bietet git auch angenehme Suchfunktionen:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log --author="Eric" --grep="database"
git log --since="2022-01-01" --until="2023-01-01" --committer="Eric"
git log --author="Eric" --grep="database" git log --since="2022-01-01" --until="2023-01-01" --committer="Eric"
git log --author="Eric" --grep="database"
git log --since="2022-01-01" --until="2023-01-01" --committer="Eric"

Und vielleicht will ich einfach nur die letzten zwei Commits haben, die eine bestimmte Datei verändert haben?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log -p -2 MasterManagerContainerManagerContainer.java
git log -p -2 MasterManagerContainerManagerContainer.java
git log -p -2 MasterManagerContainerManagerContainer.java

Mach das weg

git reset --soft
git reset --soft?
git reset --hard
git reset --hard?
git revert
git revert?
git checkout <file>
git checkout <file>? Keine Sorge, ich bin ja da.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git reset --soft HEAD~2
git reset --soft HEAD~2
git reset --soft HEAD~2

Ändere nix an den Dateien, gehe aber zwei Commits zurück.

„Hä?“ Das ist nützlich, um Commits aufzuräumen und anders zu verpacken.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git reset --hard HEAD~2
git reset --hard HEAD~2
git reset --hard HEAD~2

Zurück in die Vergangenheit, als das Leben noch einfach war. Alles was nicht committet ist, wird gelöscht. Wir sind jetzt zwei Commits zurück, genau so, wie es vor zwei Commits war.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git reset
git reset
git reset

Alles was wir mit

git add
git add ge-stage-t haben, ist jetzt wieder unstaged.
git reset <file>
git reset <file> macht das gleiche mit einer bestimmten Datei.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git revert 50a5cbda3c
git revert 50a5cbda3c
git revert 50a5cbda3c

Mach einen neuen Commit, der genau die Änderungen aus diesem anderen Commit ungeschehen macht.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git checkout -- MasterManagerContainerManagerContainer.java
git checkout -- MasterManagerContainerManagerContainer.java
git checkout -- MasterManagerContainerManagerContainer.java

Stampfe alle Änderungen an dieser Datei ein und bringe sie wieder auf den letzten comitteten Stand.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git checkout .
git checkout .
git checkout .

Rage quit. Alle Änderungen verwerfen, zurück zum letzten committeten Stand.

Zeitreise!

Seit wann gibt es denn einen Bug? Zeit für eine semi-automatische Binärsuche:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect start
git bisect start
git bisect start

Wir testen den Stand und sagen dann entweder

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect good
git bisect good
git bisect good

oder 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect bad
git bisect bad
git bisect bad

bis wir den Commit gefunden haben, bei dem sich ein Bug eingeschlichen hat.

Ich bin ja faul.

Ich schreibe einfach ein Skript.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect run tolles_test_skript.sh
git bisect run tolles_test_skript.sh
git bisect run tolles_test_skript.sh

Das Skript muss 0 für gute Commits und Nicht-0 für schlechte Commits zurückgeben und git macht den Rest.

Hail Hydra!

Schon mal erlebt? Ihr habt zwei Branches im Review, arbeitet am nächsten Ticket im eigenen Branch und bekommt Feedback in Richtung „Das hier noch kurz ändern, bitte gleich, dann können wir es sofort mergen“?

Aaaah. Alles stashen und wegpacken und argh. Und jetzt soll ich auch noch schnell auf Branch 2 was testen, ob es bei mir geht?

Wie die Theorie oben schon vermuten lässt, ist der aktuelle Zustand des Dateisystems in einem Repo das Ergebnis zusammengebastelter Git-Objekte. Man kann dasselbe Repo ja auch mehrfach clonen. Kann man vielleicht aus demselben Repo mehrere Dateisysteme machen und muss dann main nur einmal pullen?

Aber ja.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git clone [URL] projekt
cd projekt
git checkout main
git worktree add ../BUG-123 feature/BUG-123
cd ../BUG-123
git clone [URL] projekt cd projekt git checkout main git worktree add ../BUG-123 feature/BUG-123 cd ../BUG-123
git clone [URL] projekt
cd projekt
git checkout main
git worktree add ../BUG-123 feature/BUG-123
cd ../BUG-123

Jeder Worktree

git worktree list
git worktree list ist ein eigener, vollständiger Verzeichnisbaum mit eigenem Index und HEAD.

Mit

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rm -rf ../BUG-123
git worktree prune
rm -rf ../BUG-123 git worktree prune
rm -rf ../BUG-123
git worktree prune

wird man einen Worktree wieder los.

Das ist fantastisch, wenn man schnell zwischen Branches wechseln möchte.

Vergangenheit, Gegenwart und Zukunft

Wenn mich Werkstudierende und Juniors fragen was sie lernen sollen, schlage ich immer zuerst Git vor. Es ist kein gutes Gefühl, die Arbeit von Stunden zu verlieren oder nach einem Rebase oder Merge in einem komplett ungeklärten Zustand zu hängen.

Ich habe mich auch nicht sonderlich kompetent gefühlt als ich ein Repo neu ge-clone-t und dann meine Änderungen manuell rein-pastiert habe. (Die meisten von uns waren wahrscheinlich schon einmal an diesen Punkt.)

Beim Debuggen von „Git-Foo“ helfen zu können ist ein nützlicher Skill, den man in jedes Team einbringen kann – ganz gleich was sie sonst für Technologien einsetzen.

Shell-Weisheit des Tages
One of us! One of us!

Als ich meinen ersten Computer hatte (486er mit MS-DOS), hatte ich kein Internet, kein Handbuch und nur meinen grummeligen Vater, der mir hier und da ein paar Sachen erklärt hat. Es ist leicht, sich alleine zu fühlen – auch heute, wenn man jedes Problem googlen oder von einer LLM-AI erklären lassen kann.

Aber man muss nicht alleine sein!

Es gibt Foren, Subreddits, IRC Channel, GitHub, unzählige soziale Medien und (gruselig, ich weiß) unzählige Meetups und den einen oder anderen Verein in der analogen Welt. Ganz zu schweigen von … Arbeitskollegen!

Hätte ich nicht einen Kollegen im Team gehabt der mir gut zugeredet hätte (Hallo Andi!), würde ich heute kein Arch Linux verwenden.

Was ich wieder und wieder in den letzten 15 Jahren gelernt habe, ist dass die meisten Nerds unglaublich hilfsbereit sind und sehr gerne ihr Wissen teilen. Diese willkommenheißende Atmosphäre hat sich in den letzten Jahren immer stärker kodifiziert. Wir haben Codes of Coduct, wir haben Diversity als Kernwert von unzähligen Konferenzen und Unternehmen.

Es ist etwas, was ich unbedingt noch hier reinschreiben will, auch wenn es vielleicht für die meisten vollkommen offensichtlich ist: Es gibt ein paar arrogante Nerds, die sich auf ihrer kleinen Eisscholle aus Kompetenz einen Bunker bauen und alle runterschubsen, die an ihre Tür klopfen. Computer funktionieren aber nicht mit Magie. Man braucht auch keine Zertifikate oder Abschlüsse, um „dazu zu gehören“ (Vollzeitanstellung zu einem vernünftigen Gehalt mal außen vor gelassen.) Meistens reicht ein ehrliches Interesse. Meistens reicht das Selbstvertrauen sagen zu können „Ich verstehe das hier noch nicht, kannst Du es mir erklären?“. Und vielleicht. Vielleicht! Hast Du liebe lesende Person ja ein paar Sachen mit anderen Leuten, die sich auf derselben Straße befinden: Neugier, Offenheit für Neues, Freiheitsliebe, Motivation etwas zu bauen, Respekt vor Kompetenz unabhängig von sozialer Herkunft, Alter, Gender, Behinderungen, etc.

Die Tür ist offen. Tritt Dir die Füße ab und komm rein.

Goodies von Mayflower

Keine Sorge – Hilfe ist nah! Melde Dich unverbindlich bei uns und wir schauen uns gemeinsam an, ob und wie wir Dich unterstützen können.

Avatar von Eric

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.