Git --distributed-even-if-your-workflow-isnt
Chapters ▾

2.3 Basi di Git - Vedere la cronologia delle commit

Vedere la cronologia delle commit

Dopo che avrai creato un po' di commit, o se hai clonato un repository che già ha la sua cronologia di commit, vorrai probabilmente guardare cos'è successo nel passato. Lo strumento essenziale e quello più potente per farlo è il comando git log.

Questi esempi usano un progetto veramente semplice chiamato simplegit che uso spesso per gli esempi. Per ottenere il progetto, esegui

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

Quando esegui git log in questo progetto, dovresti avere un output che assomiglia a questo:

$ 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

In modo predefinito, senza argomenti, git log mostra le commit fatte nel repository in ordine cronologico inverso. In questo modo la commit più recente è la prima ad apparire. Come vedi, questo comando elenca ogni commit con il suo codice SHA-1, il nome e l'email dell'autore, la data di salvataggio e il messaggio della commit.

Sono disponibili moltissime opzioni da passare al comando git log per vedere esattamente quello che stai cercando. Qui ne vedremo alcune tra quelle più usate.

Una delle opzioni più utili è -p, che mostra le differenze introdotte da ciascuna commit. Puoi usare anche -2, che limita l'output agli ultimi due elementi:

$ 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

Quest'opzione mostra le stessi informazioni ma ciascun elemento è seguito dalle differenze. Questo è molto utile per revisionare il codice o per dare un'occhiata veloce a cosa è successo in una serie di commit che un collaboratore ha aggiunto.

Qualche volta è più semplice verificare le singole modifiche piuttosto che intere righe. Per questo in Git è disponibile l'opzione --word-diff, che puoi aggiungere al comando git log -p per vedere le differenze tra le parole invece di quella normale, linea per linea. Il formato word diff è piuttosto inutile quando applicato al codice sorgente, ma diventa utile quando applicato a grandi file di testo, come i libri o la tua tesi. Ecco un esempio:

$ 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"

Come puoi vedere, non ci sono righe aggiunte o rimosse in questo output, come in una normale differenza. I cambiamente sono invece mostrati sulla stessa riga. Puoi vedere la parola aggiunta racchiusa tra {+ +} e quella rimossa tra [- -]. Potresti anche volere ridurre le solite tre righe di contesto dall'output delle differenze a una sola, poiché ora il contesto è costituito da parole e non righe. Puoi farlo con -U1, come abbiamo fatto nell'esempio qui sopra.

Puoi usare anche una serie di opzioni di riassunto con git log. Per esempio, se vuoi vedere alcune statistiche brevi per ciascuna commit, puoi usare l'opzione --stat:

$ 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(+)

Come puoi vedere, l'opzione --stat visualizza sotto ogni commit la lista dei file modificati, quanti file sono stati modificati, e quante righe in questi file sono state aggiunte e rimosse. Alla fine aggiunge anche un resoconto delle informazioni. Un'altra opzione veramente utile è --pretty. Questa opzione modifica gli output di log rispetto a quella predefinita. Alcune opzioni predefinite sono pronte per l'uso. L'opzione oneline visualizza ogni commit su una singola linea, che è utile se stai controllando una lunga serie di commit. In aggiunta le opzioni short, full e fuller mostrano più o meno lo stesso output ma con più o meno informazioni, rispettivamente:

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

L'opzione più interessante è format, che ti permette di specificare la formattazione dell'output di log. Questa è specialmente utile quando stai generando un output che sarà analizzo da una macchina, perché specificando esplicitamente il formato, sai che non cambierà con gli aggiornamenti di Git:

$ 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

Table 2-1 lists some of the more useful options that format takes.

Opzione Descrizione dell'output
%H Hash della commit
%h Hash della commit abbreviato
%T Hash dell'albero
%t Hash dell'albero abbreviato
%P Hash del genitore
%p Hash del genitore abbreviato
%an Nome dell'autore
%ae e-mail dell'autore
%ad Data di commit dell'autore (il formato rispetta l'opzione --date=)
%ar Data relativa di commit dell'autore
%cn Nome di chi ha fatto la commit (committer, in inglese)
%ce e-mail di chi ha fatto la commit
%cd Data della commit
%cr Data relativa della commit
%s Oggetto

Potresti essere sorpreso dalla differenza tra autore e committer (chi ha eseguito la commit). L'autore è la persona che ha scritto la modifica, mentre il committer è l'ultima persona che ha applicato la modifica. Così, se invii una modifica a un progetto ed uno dei membri principali del progetto la applica, ne avranno entrambi il riconoscimento — tu come l'autore ed il membro del progetto come chi l'ha committata. Vedremo meglio questa distinzione nel Capitolo 5.

Le opzioni oneline e format sono particolarmente utili con un'altra opzione di log chiamata --graph. Questa aggiunge un piccolo grafico ASCII carino che mostra le diramazioni e le unioni della cronologia, che possiamo vedere nella copia del repository del progetto Grit:

$ 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

Queste sono solo alcune opzioni semplici per la formattazione dell'output di git log — ce ne sono altre. La tabella 2-2 elenca le opzioni che abbiamo visto prima e altre opzioni comunemente usate che possono essere utili per cambiare l'output del comando log.

Opzione Descrizione
-p Mostra la modifica introdotta con ogni commit.
--word-diff Mostra la modifica nel formato word diff.
--stat Mostra le statistiche per i file modificati in ogni commit.
--shortstat Mostra solo le righe cambiate/aggiunte/rimosse del comando --stat.
--name-only Mostra l'elenco dei file modificati dopo le informazione della commit.
--name-status Mostra l'elenco dei file con le informazioni aggiunte/modifiche/eliminate.
--abbrev-commit Mostra solo i primi caratteri del codice SHA-1 invece di tutti i 40.
--relative-date Mostra la data in un formato relativo (per esempio, "2 week ago", "2 settimane fa") invece di usare il formato completo della data.
--graph Mostra un grafico ASCII delle diramazioni e delle unioni della cronologia insieme all'output del log.
--pretty Mostra le commit in un formato alternativo. L'opzione include oneline, short, full, fuller, e format (quando specifichi un tuo formato).
--oneline Un'opzione di convenienza abbreviazione per --pretty=oneline --abbrev-commit.

Limita l'output del log

Oltre alle opzioni per la formattazione dell'output, git log accetta una serie di utili opzioni restrittive, ovvero opzioni che ti permettono di vedere solo alcune commit. Abbiamo già visto una opzione del genere, l'opzione -2, che mostra solamente le ultime due commit. Infatti, puoi usare -<n>, dove n è un intero, per vedere le ultime n commit. In realtà non la userai spesso, perché Git accoda tutti gli output paginandoli, così vedrai solamente una pagina alla volta.

Le opzioni temporali come --since e --until sono invece molto utili. Questo comando, per esempio, prende la lista dei commit fatti nelle ultime due settimane:

$ git log --since=2.weeks

Questo comando funziona con molti formati — puoi specificare una data (“2008-01-15”) o una data relativa come “2 years 1 day 3 minutes ago”.

Puoi inoltre filtrare l'elenco delle commit che corrispondono a dei criteri di ricerca. L'opzione --author ti permette di filtrare per uno specifico autore e l'opzione --grep ti permette di cercare delle parole chiave nei messaggi delle commit. (Nota che specifichi entrambe le opzioni il comando cercherà le commit che corrispondano a tutte le opzioni specificate.)

Se vuoi specificare più opzioni --grep alternative devi usare --all-match.

L'ultima opzione di git log per il filtraggio è il percorso. Se specifichi il nome di una directory o di un file, puoi limitare l'output del log alle sole commit che introducono modifiche a quei file. Questa è sempre l'ultima opzione specificata e generalmente è preceduta dal doppio meno (--) per separare i percorsi dalle opzioni.

Nella tabella 2-3 vediamo una lista di riferimento di queste e di altre opzioni comuni.

Opzioni Descrizione
-(n) Vedi solo le ultime n commit
--since, --after Mostra solo le commit fatte dalla data specificata.
--until, --before Mostra solo le commit fatte entro la data specificata.
--author Mostra solo le commit dell'autore specificato.
--committer Mostra solo le commit del committer specificato.

Filtrare i risultati in base a data e ora

Per sapere cosa è stato committato nel repository di Git (git://git.kernel.org/pub/scm/git/git.git) il 29/04/2014 (usando come riferimento il fuso orario impostato sul tuo computer)

$ git log --after="2014-04-29 00:00:00" --before="2014-04-29 23:59:59" \
     --pretty=fuller

Il risultato che si ottiene eseguendo questo comando cambia in base al fuso orario dove viene eseguito. È quindi consigliato usare un orario assoluto quando si usano le opzioni --after e --before, come per esempio l'ISO 8601 (che include anche informazioni sul furo orario), così da ottenere gli stessi risultati indipendentemente dal fuso orario.

Per ottenere le commit eseguite in un determinato istante (per esempio il 29 Aprile 2013 alle 17:07:22 CET), possiamo eseguire:

$ git log  --after="2013-04-29T17:07:22+0200"      \
          --before="2013-04-29T17:07:22+0200" --pretty=fuller

commit de7c201a10857e5d424dbd8db880a6f24ba250f9
Author:     Ramkumar Ramachandra <artagnon@gmail.com>
AuthorDate: Mon Apr 29 18:19:37 2013 +0530
Commit:     Junio C Hamano <gitster@pobox.com>
CommitDate: Mon Apr 29 08:07:22 2013 -0700

    git-completion.bash: lexical sorting for diff.statGraphWidth

    df44483a (diff --stat: add config option to limit graph width,
    2012-03-01) added the option diff.startGraphWidth to the list of
    configuration variables in git-completion.bash, but failed to notice
    that the list is sorted alphabetically.  Move it to its rightful place
    in the list.

    Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
    Signed-off-by: Junio C Hamano <gitster@pobox.com>

Data e ora di AuthorDate e CommitDate hanno un formato standard (--date=default) che mostra le informazioni sul fuso orario, rispettivamente, dell'autore e di chi ha eseguito la commit.

Altri formati utili sono --date=iso (ISO 8601), --date=rfc (RFC 2822), --date=raw (i secondi passati dall'1/1/1970 UTC) --date=local (l'orario del tuo attuale fuso) e --date=relative (per esempio: "2 ore fa").

Usando git log senza specificare un orario equivale a specificare l'orario attuale del tuo computer (mantenendo la differenza con l'UTC).

Per esempio, eseguendo git log alle 09:00 su un computer che sia 3 ore avanti rispetto all'UTC, rende equivalenti questi comandi:

$ git log --after=2008-06-01 --before=2008-07-01
$ git log --after="2008-06-01T09:00:00+0300" \
    --before="2008-07-01T09:00:00+0300"

Per fare un ultimo esempio, se vuoi vedere quale commit di Junio Hamano dell'ottobre del 2008 (relative al fuso di New York) che modificano i file di test nella cronologia dei sorgenti di Git che non sono ancora state unite con merge, puoi eseguire questo:

$ git log --pretty="%h - %s" --author=gitster \
    --after="2008-10-01T00:00:00-0400"         \
    --before="2008-10-31T23:59:59-0400" --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

Ci sono circa 20,000 commit nella cronologia dei sorgenti di git, questo comando mostra 6 righe che corrispondono ai termini della ricerca.

Usare una GUI per visualizzare la cronologia

Se vuoi usare uno strumento più grafico per visualizzare la cronologia delle tuoe commit, puoi provare un programma in Tck/Tk chiamato gitk che viene distribuito con Git. Gitk è fondamentalmente uno strumento grafico come git log, e accetta quasi tutte le opzioni di filtro supportate da git log. Se digiti gitk dalla riga di comando nel tuo progetto, dovresti vedere qualcosa di simile alla Figura 2-2.


Figura 2-2. Il grafico della cronologia con gitk.

Puoi vedere la cronologia delle commit, nella metà superiore, della finestra come un albero genealogico carino. La finestra delle differenza, nella metà inferiore, mostra le modifiche introdotte con ciascuna commit che selezioni.