Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/html/wp-content/plugins/iwp-client/addons.api.php on line 40

Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/html/wp-content/plugins/iwp-client/addons.api.php on line 40

Warning: session_start(): Cannot start session when headers already sent in /var/www/html/wp-content/plugins/facebook-events-widget/facebook-events-widget.php on line 40
Einige meiner Git Tips, Tricks und Arbeitsabläufe | Cocoaheads Austria

Einige meiner Git Tips, Tricks und Arbeitsabläufe

Einige meiner Git Tips, Tricks und Arbeitsabläufe

Dieses Posting ist die deutsche Übersetzung meines Originalpostings in englischer Sprache (dank an Pepi für die Übersetzung!) und basiert auf einem Vortrag den ich beim 18. Cocoaheads Meeting in Wien (CHW018) am 17. Feber 2011 gehalten habe. Es ist eine kommentierte Tour meiner Git Konfiguration, Git bezogenen Skripte und Kommandos sowie verschiedner anderer Tips und Tricks die ich mir im Laufe der Jahre angeeignet habe. Du findest die meisten dieser Dinge in meinem dotfiles Repository sowie viele andere Dinge, wie Teile meiner Zsh Konfiguration. Patches sind willkommen.

Warnung: Manche dieser Tips und Tricks sind sehr spezifisch auf meine Installation und meinen Workflow angepasst (Mac OS X Snow Leopard, Git 1.7.4). Manches mag für Dich nicht wie beschrieben funktionieren. Ich setzte grundlegende Kenntnisse der Kommandozeile voraus.

Die Basics

Hilfe zur Selbsthilfe

Egal was Du mit Git machen möchtest, es gibt immer ein Stück Dokumentation, daß Dir dabei hilft. Du solltest wissen woher Du welche Art von Hilfe beziehen kannst. Auf der Kommandozeile gibt es zwei grundsätzliche Arten.

Die Erste ist git <command> -hwobei <command> natürlich durch das entsprechende Kommando ersetzt werden muß. Git gibt Dir eine kurze Übersicht über die Syntax des Aufrufs aus, sowie die wichtigsten Optionen dazu.

Die Zweite ist ein Blick in die man Pages selbst. Der schnellste Weg sie aufzurufen ist die Eingabe von git help <command> worauf Git sie für Dich öffnet. Die man Pages gehen sehr in die Tiefe, sind aber auch nichts für schwache Nerven.

Im Web gibts es weitere Optionen. Die Git Homepage listet einige Einführungen, Tutorials, Anleitungen sowie Bücher auf, die Dir aushelfen können.

Git Autovervollständigung für Deine Shell

Ich verwende Git hauptsächlich von der Kommandozeile aus. Glücklicherweise ist es recht einfach die Git Completion für bash oder Zsh zu aktivieren. Wenn Du Homebrew verwendet hast um Git zu installieren, füge folgendes zu Deiner ~/.bashrchinzu

source `brew --prefix git`/etc/bash_completion.d/git-completion.bash

Überprüfe ob die Completion inkludiert ist wenn Du Git auf einem anderen Weg installiert hast! Falls nicht, kannst Du in jedem Fall das git repo klonen und es von dort verlinken oder kopieren.

Bash-Completion ist auch über Mac Ports erhältlich

bash-completion @1.3 (sysutils)
    Programmable bash completions

port install bash-completion

Um bash_completion zu verwenden füge die folgenden Zeilen am Ende Deiner ~/.bash_profile ein:

if [ -f /opt/local/etc/bash_completion ]; then
    . /opt/local/etc/bash_completion
fi

und auch über fink verfügbar (für altertümliche Leute)

bash-completion 20060301-3 Command-line completions for bash

fink install bash-completion

Um bash_completion zu verwenden füge die folgenden Zeilen am Ende Deiner ~/.bash_profile ein:

if [ -f /sw/etc/bash_completion ]; then
        . /sw/etc/bash_completion
fi

Wenn Du, wie ich, die Zsh verwendst ist ein wenig mehr Arbeit notwendig. Füge die folgenden Zeilen zu Deiner ~/.zshrc hinzu (und passe sie gegebenenfalls an Deine Installationsmethode an):


# bash Autovervollständigung für Git
# Damit kann git-completion korrekt arbeiten
autoload bashcompinit
bashcompinit
source `brew prefix`/etc/git-completion.d

Update 2011-04-05: Wenn du eine neuere ZSH verwendest (Ich habe es mit der Version 4.3.9 getestet), kannst du alles eben geschriebene ignorieren, da die Git-Autovervollständigung einfach so funktionieren wird (sofern du bereits

autoload -U compinit && compinit

in deiner

.zshrc

hast)

Ein Alias von ‘g’ nach git

Ich lege mir einen ‘g’ Alias für git an, was mir 66% Tipparbeit spart. Außerdem bin ich faul. Füge folgende Zeile zu Deiner ~/.bashrc or ~/.zshrc hinzu:

alias g='git'

Damit die Autovervollständigung nicht nur mit dem git Kommando, sondern auch mit dem g Alias funktioniert sind folgende Zeilene notwendig.

# Autocomplete for 'g' as well
complete -o default -o nospace -F _git g

git-config(1)

Hier werden alle globalen Git Einstellungen gespeichert. Die ~/.gitconfig verwendet das INI Dateiformat um Einstellungen zu speichern, Du kannst aber auch git config verwenden um Einstellungen abzufragen oder zu setzen.
Die grundlegendsten Einstellungen die Du vornehmen solltest sind Dein Name und Email Adresse. Beide werden in jedem Commit den Du machst enthalten sein.

git config --global user.name "Markus Prinz"
git config --global user.email "markus.prinz@nuclearsquid.com"

Ich erzähle Dir natürlich nicht wie Du Dein Leben zu führen hast, aber Du wirst wahrscheinlich Deinen eigenen Namen und Deine eigene Emailadresse anstatt meiner verwenden wollen.

Mit der Zeit wirst Du Dir Deinen eigenen Satz an Einstellungen zusammenstellen um Git nach Deinem Geschmack anzupassen. Hier sind einige Einstellungen die ich nützlich finde:

  • Erlaube Git Kommandos farbige Ausgaben, falls möglich

    git config —global color.ui auto

  • Deaktiviere die Belehrungen durch Git wenn Du etwas pushen möchtest was nicht fast forwardable ist.

    git config —global advice.pushNonFastForward false

  • Deaktiviere die “how to stage/unstage/add” Tips durch git status:

    git config —global advice.statusHints false

  • Teile Git mit welche Whitespace Probleme es erkennen soll. Konkret jeglichen Whitespace am Zeilenende und Mischung von Leerzeichen und Tabulatoren.

    git config —global core.whitespace trailing-space,space-before-tab

    Beachte die man Page für weitere mögliche Optionen.

  • Erlaube git diffdie Erkennung einfacher Umbenennungen und Kopien:

    git config —global diff.renames copies

  • Verwende in git diff mnemonische Präfixe (index, work tree, commit, object) anstatt der üblichen a und b Notation:

    git config —global diff.mnemonicprefix true

  • Lasse den lokalen Branch automatisch den remote Branch tracken wenn Du einen remote Branch erstellst:

    git config —global branch.autosetupmerge true

  • Pushe den aktuellen Branch zum Upstream Branch wenn DU ohne Angabe einer refspec pushst. Beachte die man Page zu git configfür weitere mögliche Optionen.

    git config —global push.default tracking

  • Aktiviere die Aufzeichnung von Konfliktlösungen damit identische Codebrocken später automatisch gelöst werden können.

    git config —global rerere.enabled true

    Sieh Dir auch die Einstellungen rerere.autoupdate an.

  • Zeige immer ein diffstat am Ende eines Merge an:

    git config —global merge.stat true

Dir ist sicherlich aufgefallen, daß ich für jeden Aufrunf von git config die --global Option verwendet habe. Der Grund dafür ist, daß Git nicht nur Deine globale gitconfig (in ~/.gitconfig) beachtet sondern auch Repository spezifische Konfigurationen (in .git/config). So kannst Du jedes Repository Deinen eigenen Bedürfnissen anpassen. Verwende dazu git config innerhalb Deines Repositories ohne die --global Option.

gitignore(5)

Man möchte nicht immer alle Dateien ins Repository einchecken. Dinge wie temporäre Dateien, Logs, gerätespezifische Konfigurationen, Testdateien, Private Keys für Codesigning oder auch Dateien die leicht neu generiert werden können gehören nicht in ein Repository. Sie werden allerdings immer aufgelistet wenn man git status verwendet, auch wenn sie zu diesem Zeitpunkt nur störend sind.

Aus diesem Grund kannst Du in einer Datei namens .gitignore spezifizieren welche Dateien von Git ignoriert werden sollen. Im Wurzelverzeichnis des Repositories kannst Du mit glob patterns “Glob Patterns”) angeben welche Dateien von Git ignoriert werden sollen. Alle passenden Dateien (und Ordner) scheinen dann in git status nicht mehr auf. Man kann diese Dateien auch nicht mehr mit git add zum Repository hinzufügen (außer man zwingt Git mit dem -f Flag dazu).

In Rails 3 Projekten ist folgende .gitignore die Standardeinstellung:

.bundle
db/*.sqlite3
log/*.log
tmp/

Dies teilt Git mit das .bundleVerzeichnis, alle Dateien die in .sqlite3 im db Verzeichnis und alle .log Dateien in log, sowie alles im tmp Verzeichnis zu ignorieren.

Abhängig von Deinem Projekt kannst Du auch Deine eigenen Patterns angeben. Auf Github existiert ein Repository mit Glob Patterns für viele Programmiersprachen, Frameworks und Platformen.

Es gibt auch .gitignore Vorlagen speziell für Xcode 3 & 4.

Du kannst Git auch anweisen eine globale .gitignore Datei zu beachten wo Du für Deine Platform spezifische oder oft Exkludierte Dateien eintragen kannst. Ich verwalte meine in ~/.gitignore und es enthält Ausschlüsse wie .DS_Store und Icon?. Du mußt Git nur mitteilen wo es diese Datei finden kann:

git config --global core.excludesfile '~/.gitignore'

Ab sofort mußt Du Dir keine Sorgen mehr darüber machen, daß eine dieser Dateien in git status aufscheint.

gitattributes(5)

In dieser Datei kannst Du spezifische Attribute für Pfade angeben. Dies kann extrem praktisch sein, auch wenn man dieses Feature nicht allzu oft benötigen wird.

Die db/schma.rb Datei führt beispielsweise in Rails Projekten oft zu Merge Konflikten die einfach dadurch gelöst werden können, indem man herausfindet welche die neueste Datei ist und diese zur Konfliktlösung heranzieht. Ansatt dies jedes Mal manuell zu tun sagen wir Git das für uns zu erledigen.

Zuerst mußt Du diese Zeilen zu Deiner ~/.gitconfig hinzufügen:

[merge "railsschema"]
        name = newer Rails schema version
        driver = "ruby -e '\n\
                system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n\
                b = File.read(%(%A))\n\
                b.sub!(/^<+ .*\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n=+\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n>+ .*/) do\n\
                  %(ActiveRecord::Schema.define(:version => #{[$1, $2].max}) do)\n\
                end\n\
                File.open(%(%A), %(w)) {|f| f.write(b)}\n\
                exit 1 if b.include?(%(<)*%L)'"

Damit wird eine neue Merge Strategie namens railsschema, mit den Kommandos die Git benötigt um den Konflikt zu lösen, angelegt. Das Kommando selbst ist nur ein Stückchen Ruby Code welches herausfindet welche der schema.dbDateien die neueste ist.

Füge zu Deiner ~/.gitattributes Datei folgende Zeile hinzu:

db/schema.rb merge=railsschema

Damit weis Git, daß es beim Mergen von db/schema.rb Dateien diese spezielle Merge Startegie verwenden soll. Damit müssen wir das nie wieder manuell tun.

Man kann diese Datei auch zur Konvertierung von Zeilenende bestimmter Dateitypen verwenden oder welche Dateien es als Binärdaten behandeln soll. Konsultiere die man Page für weitere Informationen.

githooks(5)

In den meisten Fällen hast Du die Möglichkeit eigene Aktionen ausführen zu lassen, jeweils bevor und nachdem Git seine Arbeit erledigt. Ein Beispiel dafür ist der git-commit Hook. Damit könntest Du einen Commit unterbinden wenn dieser nicht bestimmte Bedingungen erfüllt.

Alle Hooks sind auführbare Dateien und können beliebiger Natur sein. Ich verwende üblicherweise Shell Skripte für die einfachen Aufgaben und Ruby für komplexere Hooks.

Jedes Git Repository enthält eine Menge von Beispielen in .git/hooks. Um einen Hook zu aktivieren genügt es eine Ausführbare Datei oder einen Symlink mit dem Namen des gewünschten Hooks in .git/hooks/ anzulegen.

Manche Hooks erhalten bestimmte Parameter die dabei helfen festzustellen was passiert ist. Der post-checkout Hook bekommt beispielsweise eine Referenz auf den vorhergehenden head, den neuen head sowie ein Flag ob der Checkout aufgrund des Wechsels von Branches erfolgt ist.

Die githooks(5) man Page listet alle verfügbaren Hooks auf. Die Interessantensten sind wahrscheinlich diese:

  • pre-commit: Dieser Hook wird bevor git commit einen neuen Commit erstellt aufgerufen. Wenn dieser Hook einen Rückgabewert ungleich Null zurückgibt wird der Commit abgebrochen. Dies ist nützlich um Leute davon abzuhalten Dinge einzuchecken die im Repository nichts verloren haben (wie zum Beispiel Passwörter) oder um Sachen wie Syntax Checks laufen zu lassen um sicherzustellen, daß nur gültiger Code eingechecked wird.
  • post-commit: Wird aufgerufen nachdem ein neuer Commit erstellt wurde, was beispielsweise für Benachrichtigungen praktisch ist.
  • post-checkout: Wird nach jeglicher Checkout Operation, wie dem Wechseln des Branches, aufgerufen. Ich verwende diese Hook in Rails Projekten um festzustellen ob das Gemfile verändert wurde und rufe gegebenenfalls bundle install auf.

Verwenden externer Diff Applikationen (Kaleidoscope)

Die eingebaute Diff Anzeige ist für die meisten Fälle gut geeignet. Manchmal möchte man jedoch auch Unterschiede von Nicht-Text Dateien sehen um eine bessere Übersicht zu bekommen was passiert.

Zu diesem Zweck unterstützt Git externe Diff Programme. Ich verwende das hervorragende Kaleidoscope (leider nur für Mac OS X) welches nicht nur Texte sondern auch Bilder anzeigen kann. Eine andere Möglichkeit wäre FileMerge.app aus den Apple Developer Tools.

Der Einsatz eines externen Diff Programmes erfordert meist die Installation eines Command Line Tools oder Skriptes. Im Fall von Kaleidoskope muß man nur den Anweisungen unter Kaleidsokope->Integration folgen um die benötigten Zeilen in der .gitconfigzu ergänzen und das Command Line Tool zu installieren.

Im wesentlichen werden dabei folgende Einträge in Deiner .gitconfig erstellt:

[difftool]
    prompt = false

[difftool "Kaleidoscope"]
    cmd = ksdiff-wrapper git \"$LOCAL\" \"$REMOTE\"

Alles was ich nun tun muß ist statt git diff nun git difftool aufzurufen und Git startet Kaleidoskope für mich. (difftool akzeptiert dabei die selben Optionen wie diff).

Git template

Wenn man mit Git ein neues Repository anlegt verwendet es dazu eine Vorlage um Standarddateien anzulegen. Unter der Annahme, daß Git bei Dir in /usr/local installiert ist, findest Du diese Templates in /usr/local/share/git-core/templates. Das ist vor allem praktisch um Deine eigenen Hooks zu installieren. So mußt Du nicht mehr daran denken sie bei jedem neuen Repository von Hand hinzuzufügen.

Falls Du das Template nach der Erstellung eines Repositories geändert hast, kannst Du die Änderungen durch den Aufruf von git init innerhalb des Repositories übernehmen.

Externe Programme

Es gibt einige grafische Programme die dir das Leben mit Git vereinfachen. Insbesondere wenn es um die Darstellung der History geht. Es folgen einige Beispiele:

GitX

GitX ist mein persönlicher Favorit wenn es darum geht die History eines Repositories zu erkunden. Es zeigt alle Diffs eines Commits an, erlaubt Commits zu durchsuchen und hält sogar einige nette Funktionen bereit um auf GitX neue Commits zu erstellen. Dadurch ist es ein gutes Werkzeug für Leute die grafische Oberflächen gegenüber der Kommandozeile bevorzugen. GitX ist OpenSource und auf GitHub verfügbar.

Die offizielle Version ist leider ein wenig veraltet. Nimm also stattdessen Brotherbard’s verbesserten GitX Fork welcher einige neue Features und Verbesserungen gegenüber dem Original aufweist.

Gitbox

Gitbox (Auch im Mac App Store verfügbar ist eine weitere Mac Applikation die mehr Funktionalität als GitX bietet. Ich selbst habe es nicht so viel verwendet und kann daher leider nicht viel darüber sagen. Es ist für bis zu drei Repositories kostenlos, es kann also nicht schaden es auszuprobieren. Die Vollversion kostet 39,- $US.

Tower

Tower ist sehr viel mächtiger als als GitX und Gitbox, allerdings auch komplexer. Wenn Du die Kommandozeile überhaupt nicht magst, dann könnte das der richige Client für Dich sein. Tower kostet 49,- € und kann für 30 Tage kostenlos getestet werden

iOS Apps

Es existieren auch ein paar Apps für iPhone und iPad die sich allerdings, mit der Ausnahme von Cardff, darauf beschränken Deinen GitHub Account und Repositories anzuzeigen.

(Anmerkung: Die App Store Links sind Affiliate Links. Der Erlös kommt Cocoaheads Austria zugute.)

  • iOctocat (€ 3.99, für iPhone, Source)

    Ein kompletter GitHub Begleiter für Dein iPhone. Du kannst damit Feeds, Repositories und Benutzerprofile ansehen. Man kann damit ebenfalls Issues eröffnen, bearbeiten und schließen.

  • Cardff Git 1.7 (€ 0.79, für iPhone)

    Ein kleiner Schummelzettel als handliche Referenz für die meisten Git Kommandos.

  • GitHub Viewer (€ 0.79, für iPad)

    Ermöglicht Dir Deine GitHub Repositories, History und Benutzerprofile auf GitHub einzusehen.

  • GitHub Viewer Light (für iPad)

    Sehr ähnlich wie Github Viewer, kann allerdings keine privaten Repositories anzeigen.

  • githood (€ 0.79, für iPhone)

    Ermöglicht es Dir Repositories inklusive Diffs und Feeds auf GitHub einzusehen.

Stacked Git (stGit)

Im Gegensatz zu den anderen Programmen ist Stacked Git keine grafische Applikation sondern ein Kommandozeilentool. Es erweitert Git indem es ermöglicht einen Stapel von Patches auf dem Repository zu verwalten. Alle Patches werden als normale Commits gespeichert. StGit hilft dabei sie zu verwalten.

Ich habe dieses Programm recht viel verwendet. Inzwischen wurde es jedoch von git rebase und git stash abgelöst.

Revisionen

Die meisten Git Kommandos verlangen ein Revision. Für Gewöhnlich übergibt man den Namen eines Branches oder die SHA1 Prüfsumme eines bestimmten Commits. Git Revisionen können jedoch viel mehr als das.

Zunächst gibt es eine spezielle Revision die head (oder auch HEAD) genannt wird. Sie ist zeigt auf die Revision die gerade in Deiner Arbeitskopie aktuell ist, in den meisten Fällen die letzte Revision eines Branches.

Zusätzlich kannst Du damit, ausgehend von einer Referenz, ältere Commits ansprechen ohne ihre SHA1 Prüfsumme kennen zu müssen. Nehmen wir an, daß Du den Commit vor dem aktuellen ansprechen möchtest. (Dieser wird auch “Parent” genannt.) Gib dazu einfach head^ ein. Den Urgroßvater (“great-grandfather”) erreichst Du mit head^^^. Je weiter Du in der Geschichte zurückgehst umso unübersichtlicher wird es. Git erlaubt auch eine alternative Syntax. Der Urgroßvater wird so zu head~3.

Möchtest Du einen Commit ansprechen der einen bestimmten Text enthält? Benutze :/really awesome commit um diesen really awesome commit zu finden.

branch@{yesterday} gibt Dir die Resivion bei der branch gestern war. branch@{2 days 3 hours 4 seconds ago} ist der Branch von, naja vor 2 Tagen, 3 Stunden und 4 Sekunden.

Ich kratze hier nur an der Oberfläche von dem was möglich ist. Lies also die man Pages!

Commands

Hier sind einige Kommandos die Du vielleicht noch nicht kennst oder deren Optionen Dir noch unbekannt waren.

git-stash(1)

git stash kann extrem praktisch sein. Die Funktionalität ist sehr einfach. Wenn Du git stash save aufrufst, nimmt es alle Änderungen in Deinem Working Directory und den Index, speichert sie weg und hinterlässt ein sauberes Working Directory. Nachdem Du erledigt hast, was Du tun wolltest kannst Du Deine Änderungen mit git stash pop zurückholen.

Es kommt noch besser. Nachdem git stash Deine Änderungen in einem Commit speichert kannst Du mehrfach stashen und bekommst so eine hübsche Liste mit Deinen gestashten Änderungen. git stash beachtet dabei auch in welchem Branch Du Deine Änderungen gestashed hast. Dadurch weist Du genau welche Änderungen Du wiederherstellen möchtest.

git stash list zeigt Die alle Stashes die Du gesichert hast. git stash apply commited den obersten Stash Deines Stash Stapels in Dein Working Directory. git stash pop entfernt zusätzlich zu apply den gestashten Commit. git stash clear löscht alle Deine Stashes und git stash drop ermögilcht Dir einen einzelnen Stash zu entfernen.

git-rebase(1)

git rebase kann ein kniffliges Kommando sein obwohl der Zweck ein einfacher ist. Mit einem Bereich von Commits und einem Startpunkt verschiebt es die angegebenen Commits zum genannten Startpunkt wobei die Commits selbst und eine lineare History erhalten bleiben. (Also kein Merge Commit.)

In den meisten Fällen wirst Du nur eine einzelne Referenz übergeben, nämlich den Branch auf den Du Deinen aktuellen Branch rebasen möchtest. git rebase stellt fest wo die beiden Branches voneinander abgezweigt und benutzte dies als Begin des Commit Bereiches. Falls Du ein zweites Argument angibst schaltet git rebase zuerst auf diesen Branch um bevor Deine Commits verschiebt.

Ein sehr praktisches Flag ist --interactive. Wenn Du dieses Flag übergibst öffnet Git Deinen bevorzugten Text-Editor mit einer Liste aller Commits die übertragen werden. Alle Commits sind zunächst mit einem pick Prefix versehen. Du kannst nun Commits komplett fallen lassen indem Du die Zeile löschst oder sie nach Deinem Geschmack umsortieren. Du kannst auch edit verwenden um einen Commit zu bearbeiten oder zu korrigieren. (Damit kann man Commits auch aufteilen.) Mit reword kannst Du die Commit Nachricht bearbeiten und mit squash oder fixup kannst Du einen Commit mit dem vorgeheneden zusammenführen wobei fixup die vorhergehende Commit Nachricht wiederverwendet, wohingegen squashDir erlaubt eine neue Commit Nachricht zu editieren.

Der Interaktive Modus ist dann sehr praktisch wenn Du an einer Idee arbeitest, einen Commit machst, und später draufkommst, daß es doch nicht so gut funktioniert. Du commitest einen Fix, aber der Commit der damit korrigiert wird, ist jedoch bereits unter anderen Commits vergraben, wodurch Du das nicht einfach so korrigieren kannst. git rebase --interactive ermöglicht Dir diese beiden Commits zusammenzuführen, damit es so aussieht als ob Du den Fehler garnicht gemacht hast.

git-push(1)

Du verwendest dieses Kommando wahrscheinlich schon regelmäßig, es hat jedoch in den letzten Versionen einige praktische Optionen dazubekommen.

  • -u oder --set-upstream: -u teilt Git mit, daß der remote-branch der standard upstream Branch sein soll. Dann kannst Du git push <repo> local-branch:remote-branch in Zukunft durch ein simples git push ersetzen.
  • --delete: Löscht die angegebenen Refs im remote Repository anstatt sie zu pushen.

git-reflog(1)

Jede Operation die die Spitze eines Branches verändert wird von Reflog aufgezeichnet. Mit git reflog kannst Du diese Informationen ansehen.

Das ist nicht nur praktisch sondern auch potentiell lebensrettend nach Kommandos wie git rebase oder git filter-branch die (scheinbar) die Geschichte Deines Repositorys verändern. Während diese Kommandos tatsächlich Commits ändern, so werden die alten Commits jedoch nicht gelöscht sondern sind lediglich nicht mehr auf normalem Weg erreichbar. (Das ist auch der Grund dafür, daß Git gelegentelich git gc auf Dein Repository anwendet.) Trotzdem kannst Du mit dem Reflog Mechanismus die SHA1 Prüfsummen dieser Commits herausfinden und sie bei bedarf zurückholen.

Mittels git reflog kannst Du die gesamte Reflog Information anzeigen lassen. Du kannst mit der Syntax head@{0} jeden Punkt im Reflog ansprechen. Wobei head für jede Art von Referenz steht und 0 den ersten Eintrag im Reflog anspricht. Wenn Du beispielsweise ein rebase gemacht hast, kannst den dadurch zerstörten Branch mit dem Kommando git reset head{0} wieder in den Zustand vor dem Rebase zurückversetzen.

diffstats

git diff --stat
git log --stat

Dem ist nichts weiter hinzuzufügen.

Skripte

Ich verwende einige Skripte in meinem Git Workflow. Die meisten davon habe ich von irgendwo kopiert und in Folge an meine eigenen Bedürfnisse angepasst. Die jeweils aktuellen Versionen findest Du in meinem dotfiles Repository.

Ein weiterer Trick der vielen Leuten nicht bekannt ist: Wenn Du ein Skript namens git-foo in Deinem $PATH hast und git foo aufrufst wird Git tatsächlich automatisch Dein Skript aufrufen. Damit kannst Du Git um scheinbar interne Kommandos erweitern und mußt Dich nicht um Aliase wie g sorgen.

git-up, git-reup

git pull zeigt Dir nur an, daß es neue Commits gibt und gibt Dir ein diffstat um anzuzeigen welche Dateien geändert wurden. git up tut ein wenig mehr. Es holt nicht nur neue Commits sondern zeigt auch ein shortlog aller neuen Commits an und liefert Dir somit einen schnellen Überblick über die neuen Commits.

Wenn Du es als git reup aufrufst passiert im wesentlichen das Selbe mit einem Unterschied. Anstatt eines git pull wird ein git rebase ausgeführt womit eine lineare History erhalten bleibt. Falls Du ein dirty Working Directory hast werden vorher noch mittels git stash Deine Änderungen gesichert und nachher mit git stash pop danach restauriert.

git-new-workdir

Manchmal möchtest Du zwei Branches Deines Repositorys ausgechecked haben. Ein Lösungsweg wäre einfach einen zweiten lokalen Clone Deines Repos zu erstellen und dort den anderen Branch auszuchecken. Dadurch mußt Du nun allerdings zwei Repos in Sync mit dem Original halten nachdem Commits im einen Repo nicht automatisch in das zweite propagiert werden.

Hier kommt git new-workdir ins Spiel. Du übergibst den Pfad zum original Repository und den Pfad wo das zweite Repository erstellt werden soll. Optional kannst Du auch angeben welcher Branch im zweiten Repository verwendet werden soll. git new-workdir erstllt dann ein zweites Repository welches ein zweiter Checkout ist. Somit mußt Du nicht mehr pushen oder pullen um beide Repositories synchron zu halten.

git-wtf

Das ist ein sehr praktisches Skript. Rufe es in einem Branch auf der einen Remote Branch hat und es sagt Dir ob sie in Sync sind oder welche Commits in jedem Branch fehlen. So bekommst Du schnell einen Überblick über den Status Deines lokalen und remote Branches.

Beispiel Ausgabe:

Local branch: master
[ ] NOT in sync with remote (you should push)
    - Add vim-endwise [af43b93]
    - Update vim plugins [a926c01]
    - Add TComment plugin for Vim [035d3b9]
    - Add matchit plugin for Vim [7b76c04]
    - Add vim-markdown plugin [a6e7c19]
    ... and 3 more (use -A to see all).
Remote branch: origin/master (git@github.com:cypher/dotfiles.git)
[x] in sync with local

NOTE: working directory contains modified files.

Aliase

Einige der Aliase die ich verwende die nicht direkte Abkürzungen von üblichen Git Operationen sind.

git l

Zeigt eine einzeilige History des aktuellen Branches zuzuzüglich der Der Ref Names von Commits.

git lg

Ähnlich wie git l visualisiert jedoch Branches und Merges mit der Graph Darstellung.

git ls-ignored

Listet alle Dateien auf die aufgrund von .gitignore ignoriert werden.

git amend

Kombiniert den letzten Commit mit Deinen lokalen Änderungen die gestaged sind ohne die letzte Commit Message zu ändern.

git wd, git wds

Zeigt ein Diff welches nicht nur die geänderten Zeilen kennzeichnet, sondern auch die exakten Zeichen innerhalb der jeweiligen Zeile. git wds tut das Selbe für gestagete Änderungen.

GitHub

GitHub spezifische Tools und Workflows die ich verwende.

github gem

Über RubyGems wie folgt zu installieren:

gem install github
# Use sudo if necessary

Mittels github kannst Du mit GitHub interagieren ohne den Komfort Deiner Kommandozeile verlassen zu müssen. Darunter Funktionen wie die Erstellung neuer Repositories, die Forks eines Repositorys zu holen, forken, Pull Requests erzeugen und viele weitere.

Es installiert sich selbst als github und gh wobei die Parameter -h bzw. --help alle verfügbaren Kommandos auflistet.

Falls Du Ruby 1.9 verwendest kann es passieren, daß die Installation mit einer Fehlermeldung wie text-hyphen requires Ruby version < 1.9 fehlschlägt. Wenn Du --force an gem übergibst wird es trotzdem installiert. Bisher hatte ich noch keine Probleme damit.

git-pulls

git pulls ist ein neues Tool welches Dir beim Managen und Mergen von Pull Requests hilft.

Über RubyGems wie folgt zu installieren:

gem install git-pulls
# Use sudo if necessary

Es ist danach als git pulls <subcommand> verfügbar. Wenn Du das subcommand auslässt bekommst Du einen kurzen Überblick aller verfügbaren subcommands. list listet Dir alle offenen Pull requests auf, show zeigt Dir einen bestimmten Pull Request an und merge macht Dir einen Kaffee. (Eigentlich merged es den angegebenen Pull Request, aber das hast Du bestimmt schon vermutet, oder?)

Es wird aktiv weiterentwickelt, also halte es aktuell! (gem update git-pulls)

Pull Requests

Wenn jemand Dein Repository auf GitHub forked und seine Commits in dieses Repository pushed, möchten sie vielleicht auch, daß Du diese Änderungen zurück in Dein Repository mergest.

GitHub unterstützt diesen Workflow mittels Pull Requests. Zusätzlich zu den Commits enthält ein Pull Request auch eine Beschreibung aller Commits die in diesem Pull Request enthalten sind. Nachdem Du einen erstellt hast, können andere Leute diesen Request kommentieren, die Änderungen einsehen und sogar einzelne Zeilen kommentieren. Der Ersteller kann seinen Pull Request mit zusätzlichen Commits korrigieren um von anderen gefundene Probleme auszubessern. Dies macht Pull Requests zu einem exzellenten Code Review Werkzeug.