Learn Git

Die Commit Historie anzeigen

Nachdem Du einige Commits angelegt oder ein bestehendes Repository geklont hast, willst Du vielleicht wissen, welche Änderungen zuletzt vorgenommen wurden. Der grundlegende und mächtige Befehl, mit dem Du das tun kannst, ist git log.

Die folgende Beispiele beziehen sich auf ein sehr simples Repository mit dem Namen „simplegit“, das ich oft für Demonstationszwecke verwende:

git clone git://github.com/schacon/simplegit-progit.git

Wenn Du in diesem Projekt git log ausführst, solltest Du eine Ausgabe wie die folgende sehen:

$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

Der Befehl git log listet die Historie der Commits eines Projekts in umgekehrter chronologischer Reihenfolge auf, wenn man ihn ohne weitere Argumente ausführt, d.h. die letzten Commits stehen oben. Wie Du sehen kannst wird jeder Commit mit seiner SHA-1 Checksumme, Namen und E-Mail Adresse des Autors, dem Datum und der Commit Meldung aufgelistet.

Für den Befehl git log gibt es eine riesige Anzahl von Optionen, mit denen man sehr genau eingrenzen kann, wonach man in einer Historie sucht. Schauen wir uns also einige der am häufigsten verwendeten Optionen an.

Eine sehr nützliche Option ist -p. Sie zeigt die Änderungen an, die in einem Commit enthalten sind. Du kannst außerdem -2 angeben, wodurch nur die letzten beiden Einträge angezeigt werden:

$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,5 +5,5 @@ require 'rake/gempackagetask'
 spec = Gem::Specification.new do |s|
     s.name      =   "simplegit"
-    s.version   =   "0.1.0"
+    s.version   =   "0.1.1"
     s.author    =   "Scott Chacon"
     s.email     =   "schacon@gee-mail.com

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
     end

 end
-
-if $0 == __FILE__
-  git = SimpleGit.new
-  puts git.show
-end
\ No newline at end of file

Diese Option zeigt also im Prinzip die gleiche Information wie zuvor, aber zusätzlich zu jedem Eintrag ein Diff. Das ist nützlich, um einen Code Review zu machen oder eben mal eine Reihe von Commits durchzuschauen, die ein Mitarbeiter angelegt hat.

Manchmal ist es einfacher Änderungen an Hand der Wörter anstatt zeilenbasiert zu überprüfen. Git bietet dafür die Option --word-diff, welche man an den Befehl git log -p anhängen kann. Man weist Git damit an, einen Vergleich auf Basis der Wörter anstatt Zeile für Zeile durchzuführen. Dieser Vergleich ist ziemlich nutzlos wenn man Änderungen innerhalb von Quellcode vergleicht. Beim Vergleich von langen Textdateien zeigt er aber seine Stärke. Er bietet sich zum Beispiel für Bücher oder wissenschaftliche Texte an. Hierzu ein Beispiel:

$ git log -U1 --word-diff
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,3 @@ spec = Gem::Specification.new do |s|
    s.name      =   "simplegit"
    s.version   =   [-"0.1.0"-]{+"0.1.1"+}
    s.author    =   "Scott Chacon"

Wie man in der Ausgabe sehen kann, zeigt dieser Vergleich nicht an, welche Zeilen hinzugekommen und welche entfallen sind. Stattdessen werden Änderungen innerhalb der Zeilen dargestellt. Mit der Sequenz {+ +} wird ein neu hinzugekommes Wort gekennzeichnet, mit [- -] ein Wort, welches entfernt wurde. Normalerweise zeigt Git bei einem Vergleich drei zusätzliche Zeilen ober- und unterhalb der eigentlichen Änderung an. Bei einem Textvergleich reicht meist eine zusätzliche Zeile. Man kann dies mit der Option -U1 erreichen, so wie in dem oben gezeigten Beispiel.

Außerdem gibt es verschiedene Optionen, die nützlich sind, um Dinge zusammenzufassen. Beispielsweise kannst Du eine kurze Statistik über jeden Commit mit der Option --stat anzeigen lassen:

$ git log --stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

 Rakefile |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

 lib/simplegit.rb |    5 -----
 1 file changed, 5 deletions(-)

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

 README           |    6 ++++++
 Rakefile         |   23 +++++++++++++++++++++++
 lib/simplegit.rb |   25 +++++++++++++++++++++++++
 3 files changed, 54 insertions(+)

Die --stat Option zeigt unterhalb jedes Commits eine kurze Statistik über die jeweiligen Änderungen an: welche Dateien geändert wurden und wieviele Zeilen insgesamt hinzugefügt oder entfernt wurden. Eine weitere nützliche Option ist --pretty. Diese Option ändert das Format der Ausgabe und es gibt eine Anzahl mitgelieferter Formate. Das oneline Format listet jeden Commit in einer einzigen Zeile, was nützlich ist, wenn Du eine große Anzahl von Commits durchsuchen willst. Die short, full und fuller Formate zeigen die Commits in ähnlicher Form an, aber mit jeweils mehr oder weniger Informationen.

$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

Eines der interessantesten Formate ist format, das Dir erlaubt, Dein eigenes Format zu verwenden. Dies ist inbesondere nützlich, wenn Du die Ausgabe in ein anderes Programm einlesen willst (da Du das Format explizit angibst, kannst Du sicher sein, dass es sich nicht ändert, wenn Du Git auf eine neuere Version aktualisierst):

$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 11 months ago : changed the version number
085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code
a11bef0 - Scott Chacon, 11 months ago : first commit

Tabelle 2-1 zeigt einige nützliche Optionen, die von format akzeptiert werden:

Option    Beschreibung
%H    Commit Hash
%h    Abgekürzter Commit Hash
%T    Baum Hash
%t    Abgekürzter Baum Hash
%P    Eltern Hashs
%p    Abgekürzte Eltern Hashs
%an    Autor Name
%ae    Autor E-Mail
%ad    Autor Datum (format akzeptiert eine –-date= Option)
%ar    Autor Datum, relativ
%cn    Committer Name
%ce    Committer E-Mail
%cd    Committer Datum
%cr    Committer Datum, relativ
%s    Betreff

Du fragst Dich vielleicht, was der Unterschied zwischen Autor und Committer ist. Der Autor ist diejenige Person, die eine Änderung ursprünglich vorgenommen hat. Der Committer dagegen ist diejenige Person, die den Commit angelegt hat. D.h., wenn Du einen Patch an ein Projekt Team schickst und eines der Team Mitglieder den Patch akzeptiert und verwendet, wird beiden Anerkennung gezollt – sowohl Dir als Autor als auch dem Teammitglied als Comitter. Wir werden auf diese Unterschiedung in Kapitel 5 noch einmal genauer eingehen.

Die oneline und format Optionen können außerdem zusammen mit einer weiteren Option --graph verwendet werden. Diese Option fügt einen netten kleinen ASCII Graphen hinzu, der die Branch- und Merge-Historie des Projektes anzeigt. Das kannst Du z.B. in Deinem Klon des Grit Projekt Repositorys sehen:

$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
*  5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
*  11d191e Merge branch 'defunkt' into local

Das sind nur einige eher simple Format Optionen für die Ausgabe von git log – es gibt sehr viel mehr davon. Tabelle 2-2 listet diejenigen Optionen auf, die wir bisher besprochen haben, und einige weitere, die besonders nützlich sind:

Option    Beschreibung
-p    Zeigt den Patch, der einem Commit entspricht.
--word-diff    Führt den Vergleich Wort für Wort, anstatt Zeile für Zeile aus.
--stat    Zeigt Statistiken über die in einem Commit geänderten Dateien und eingefügten/entfernten Zeilen.
--shortstat    Zeigt nur die Kurzstatistik über eingefügte/entfernte Zeilen aus der `--stat` Option.
--name-only    Zeigt die Liste der geänderte Dateien nach der Commit Information.
--name-status    Zeigt die Liste der Dateien mit der hinzugefügt/geändert/entfernt Statistik.
--abbrev-commit    Zeigt nur die ersten Zeichen einer SHA-1 Checksumme, nicht alle 40.
--relative-date    Zeigt das Datum in relativem Format (z.B. „2 weeks ago“), nicht als vollständiges Datumsformat.
--graph    Zeigt einen ASCII Graphen der Branch- und Merge-Historie neben der Ausgabe.
--pretty    Zeigt Commits in einem alternativen Format. Gültige Optionen sind: oneline, short, full, fuller und format (mit dem Du Dein eigenes Format spezifizieren kannst)

Log Daten filtern

Zusätzlich zu den Formatierungsoptionen für die Ausgabe, akzeptiert git log eine Reihe nützlicher Optionen, um die Anzahl der ausgegebenen Commits einzuschränken. Eine solche Option haben wir bereits verwendet: die -2 Option, die bewirkt, dass nur die letzten beiden Commits angezeigt werden. D.h., Du kannst -<n> verwenden, wobei n irgendeine ganze Zahl sein kann. Im Alltag wirst Du diese Option vermutlich nicht sehr oft verwenden, weil Git die Ausgabe standardmäßig formatiert, sodass nur jeweils eine Seite anzeigt.

Darüber hinaus gibt es noch die hilfreichen Optionen --since und --until, welche die Ausgabe auf Basis der Zeitangaben eingrenzen. Beispielsweise gibt der folgende Befehl eine Liste aller Commits aus, die in den letzten zwei Wochen angelegt wurden:

$ git log --since=2.weeks

Das funktioniert mit einer Reihe von Formaten. Git akzeptiert sowohl ein vollständiges Datum („2008-01-15“) oder ein relatives Datum wie „2 years 1 day 3 minutes ago“.

Du kannst außerdem die Liste der Commits nach Suchkriterien filtern. Die --author Option erlaubt, nach einem bestimmten Autor zu suchen, und die --grep Option nach Stichworten in den Commit Meldungen. (Wenn Du sowohl nach dem Autor als auch nach Stichworten suchen willst, musst Du zusätzlich --all-match angeben – andernfalls zeigt der Befehl alle Commits, die entweder das eine oder das andere Kriterium erfüllen.)

Eine letzte sehr nützliche Option, die von git log akzeptiert wird, ist ein Pfad. Wenn Du einen Verzeichnis- oder Dateinamen angibst, kannst Du die Ausgabe auf Commits einschränken, die sich auf die jeweiligen Verzeichnisse oder Dateien beziehen. Der Pfad muss als letztes angegeben und mit einem doppelten Bindestrich (--) von den Optionen getrennt werden.

Tabelle 2-3 zeigt die besprochenen und einige weitere, übliche Optionen:

Option    Beschreibung
-(n)    Begrenzt die Ausgabe auf die letzten n commits
--since, --after    Zeigt nur Commits, die nach dem angegebenen Datum angelegt wurden.
--until, --before    Zeigt nur Commits, die vor dem angegebenen Datum angelegt wurden.
--author    Zeigt nur Commits, die von dem angegebenen Autor vorgenommen wurden.
--committer    Zeigt nur Commits, die von dem angegebenen Committer angelegt wurden.

Um beispielweise alle Commits aus der Git Quelltext Historie anzuzeigen, die alle der folgende Bedinungen erfüllen:

  • Autor des Commits ist Junio Hamano
  • Commit Datum Oktober 2008
  • Commits, welche Änderungen im Testverzeichnis beinhalten
  • Commits, welche keine Merges sind

kannst Du folgenden Befehl verwenden:

$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
   --before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attribute
acd3b9e - Enhance hold_lock_file_for_{update,append}()
f563754 - demonstrate breakage of detached checkout wi
d1a43f2 - reset --hard/read-tree --reset -u: remove un
51a94af - Fix "checkout --track -b newbranch" on detac
b0ad11e - pull: allow "git pull origin $something:$cur

Aus etwa 20.000 Commits in der Git Quellcode Historie, filtert dieser Befehl gerade einmal 6 Commits heraus, die diesen Kriterien entsprechen.

Grafische Darstellung der Historie

Wenn Dir eine grafische Anzeige der Commit Historie lieber ist, kannst Du das Tcl/Tk Programm gitk, welches mit Git ausgeliefert wird, ausprobieren. gitk ist im wesentlichen eine grafische Version von git log und akzeptiert fast alle Filteroptionen, die git log auch akzeptiert. Wenn Du gitk in einem Projekt ausführst, siehst Du etwa folgende Ausgabe:

Bild 2-2. Die gitk Oberfläche

Die Commit Historie wird in der oberen Hälfte des Fensters dargestellt. Daneben ein Graph, der die Branches und Merges zeigt. Nach Auswahl eines Commits, zeigt die Vergleichsanzeige in der unteren Hälfte des Fensters die jeweiligen Änderungen in diesem Commit.