Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Luca Manini
2015-2018
Contents
1 Introduzione 2
2 Sistemi di controllo di versione o di gestione del software 2
3 Concetti e funzionalità di base 3
3.1 Lavorare senza vcs . . . . . . . . . . . . . . . . . . . . . . . . 3
3.1.1 Singolo sviluppatore, singolo le, singola "linea di
sviluppo" . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.1.2 Singolo sviluppatore, multipli le . . . . . . . . . . . . 4
3.1.3 Singolo sviluppatore, multiple "linee di sviluppo" . . . 5
3.2 Il repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2.1 Riassunto da "comprendere git concettualmente" . . . 6
3.3 Traccia della storia dei le . . . . . . . . . . . . . . . . . . . . 7
3.4 Metadati sui le . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.5 branch e merge . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.6 repo "personali": lavoro tranquillo ed isolato . . . . . . . . . . 7
3.7 Stati e usso dei le . . . . . . . . . . . . . . . . . . . . . . . 8
4 Esempi d'uso 8
4.1 Primo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.1.1 Creazione della directory di lavoro . . . . . . . . . . . 9
4.1.2 Inizializzazione del repo . . . . . . . . . . . . . . . . . 9
4.1.3 Creazione della prima versione del programma . . . . 9
4.1.4 Controllo della situazione . . . . . . . . . . . . . . . . 10
4.1.5 Prima registrazione e commit . . . . . . . . . . . . . . 10
4.1.6 Modica, di e nuovi add e commit . . . . . . . . . . 11
4.1.7 Ritorno ad una versione precedente (solo per stampa) 14
4.1.8 Ritorno ad una versione precedente (correzione errori) 15
4.1.9 Aggiunta di una feature (su master) e x di un baco
(con merge) . . . . . . . . . . . . . . . . . . . . . . . . 16
4.2 Secondo (semplicato) . . . . . . . . . . . . . . . . . . . . . . 18
4.3 Repo remoto con due cloni . . . . . . . . . . . . . . . . . . . . 20
1
4.3.1 Creazione nuova directory top level e repo iniziali . . . 20
4.3.2 Alice crea un le con tre commit e fa un push . . . . . 21
4.3.3 Bob "scarica" i commit di Alice . . . . . . . . . . . . . 22
4.3.4 Alice fa un branch, un merge con rebase e un push . . 23
5 Workow 24
6 github 25
7 TODO Riassunto dei comandi 25
7.1 init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7.2 add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7.3 commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.4 status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.5 log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.6 checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.7 reset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.8 revert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.9 branch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.10 merge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.11 rebase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.12 fetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.13 pull . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.14 push . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.15 clean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.16 reog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
8 TODO Varie 30
8.1 .gitignore . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1 Introduzione
Questo documento è una introduzione all'uso di git (http://www.git-scm),
un sistema di controllo di versione distribuito (distributed version control
system ), scritto da Linus Torvalds (l'autore di Linux, sempre lui) nel 2005 e
distribuito con una licenza "libera" (GPL 2.0).
2
(revision control systems ) o di gestione del codice sorgente (source code man-
agement ). In questo documento li utilizzerò sempre il termine controllo di
versione e l'acronimo vcs. Sono utilizzati soprattutto per i le di testo ed in
particolare la gestione dello sviluppo del codice sorgente di un software (o
della sua documentazione o delle sue speciche o di qualsiasi altro documento
di testo), codice che in generale è formato da un insieme di le:
1. che cambia nel tempo, perché lo sto ancora scrivendo, o perché lo sto
correggendo, perché ho dei ripensamenti etc.;
2. di cui esistono "contemporaneamente" più "versioni": perché lo scrivo
per piattaforme diverse o per clienti diversi, o perché ho diverse com-
binazioni di funzionalità, perché ho una versione "di sviluppo", una di
testing e quella "uciale", perché ho quella gratuita di demo e quella
a pagamento etc.;
3. che viene creato e modicato da più persone che lavorano contempo-
raneamente sugli stessi le da posti diversi, su macchine diverse, in
momenti diversi, i cui "contributi" si devono integrare tra loro.
3
di annullare le modiche dopo un salvataggio (anche se è proprio dopo un sal-
vataggio che sarebbe più utile poterlo fare!); e poi magari non sono nemmeno
più nella stessa "sessione" di lavoro (sono già "uscito" dall'editor ).
È quindi importante poter memorizzare più versioni di uno stesso le.
Una possibilità è salvare copie del le con nomi diversi, per esempio a-0.py,
a-1.py, a-2.py etc. (alcuni editor permettono di fare questi salvataggi in
modo "automatico"). Così però la mia directory di lavoro diventa disordi-
nata. Allora potrei mettere le versioni precedenti in una directory apposta,
chiamata per esempio old. In questo modo potrei sempre "tornare indietro",
almeno per le versioni che ho salvato in old.
Spesso però i problemi si evidenziano molto tempo dopo l'introduzione
degli "errori" (soprattutto se sto lavorando senza una suite di test auto-
matici, ma questa è un'altra storia). Ma allora come faccio a sapere a quale
versione voglio "tornare"? Non ho nessuna informazione sulle "dierenze"
tra le varie versioni. Questo problema si potrebbe gestire mantenendo un
change log : un le in cui, ogni volta che salvo una nuova versione in old,
scrivo una frase di commento che indica quantomeno il motivo delle modi-
che.
Un'altra attività utile è poter "calcolare" e mostrare le dierenze tra
due le, quindi anche tra versioni dello "stesso" le. In questo modo posso
scorrere le dierenze tra le coppie di versioni "successive" e cercare la mod-
ica "colpevole" del malfunzionamento. Questo attività di diff è fonda-
mentale e vi sono centinaia di programmi in grado di farlo in modo più o
meno intelligente e comodo (magari integrandosi direttamente con l'editor
che state usando). È evidente però che per fare un diff devo disporre delle
due "versioni".
Come già accennato, la gestione delle varie versioni può essere migliorata
se ad ogni le posso associare ulteriori informazioni (dei metadati ) ad esem-
pio un'etichetta (tag ) che indica la versione del programma (non quella del
singolo le!) o delle chiavi (keyword ) o dei commenti e se poi ci sono degli
strumenti per gestire questi metadati (ad esempio per fare delle ricerche).
Dovrebbe essere chiaro a questo punto che ciò che serve è un software dedi-
cato, ossia un sistema di controllo di revisione.
4
3.1.3 Singolo sviluppatore, multiple "linee di sviluppo"
Un'altra complicazione deriva dal fatto che di uno stesso programma vi sono
normalmente più "linee di sviluppo" perché:
1. sto sviluppando varie versioni del programma per vari sistemi operativi
e mentre alcuni parti sono comuni, altre esistono in versioni speciche
per ciascun sistemi operativi;
3.2 Il repository
Anche senza sapere quasi nulla su un vcs, si può immaginare che per man-
tenere tutta l'informazione necessaria a fornire le funzionalità richieste,
questo dovrà gestire un qualche tipo di "contenitore" per i dati gestiti (i
le del codice sorgente) e per i relativi metadati (chi ha fatto cosa quando
e perché). Questo contenitore viene chiamato repository (repo per brevità).
Se il tutto viene usato da un solo utente, si può pensare ad un unico repo,
posto magari "in locale" sulla macchina "di sviluppo". Se però vi sono varie
persone che devono collaborare allora si deve scegliere normalmente tra due
possibilità:
Nel primo caso sono però vincolato ad avere accesso continuo al repository
ed inoltre "inviando" le mie modiche potrei sovrascrivere le modiche di
altri o comunque far sì che il progetto sia in uno stato non corretto. Nel
secondo caso può essere dicile conciliare le dierenze, specialmente se è
"passato molto tempo" dall'ultima "sincronizzazione".
Una delle caratteristiche di git è proprio quella di far sì che ogni utente
abbia, nel proprio repo personale, una copia completa di tutto il software
il che permette di:
5
2. non essere vincolato al "ritmo di lavoro" degli altri sviluppatori,
5. etc.
Per fare riferimento ad un commit posso sempre usare il suo nome, che
però non è molto facile da ricordare o da scrivere (anche se in generale è
suciente fornire i primi sette/otto caratteri) e non è signicativo (per gli
umani). git permette quindi di denire ed utilizzare delle "etichette" (o
intestazioni). Le più importanti sono:
6
3.3 Traccia della storia dei le
La funzionalità più ovvia di un vcs è mantenere la storia dei le (separata-
mente per ogni le oppure, come in git, considerando tutta un albero di
directory come un unico "oggetto") permettendo di "salvare" in ogni mo-
mento la versione corrente e poi proseguire nelle modiche, sapendo di poter
recuperare in ogni momento una qualsiasi delle "versioni" precedenti.
L'azione di "salvare" la versione corrente, prendendola dalla directory
di lavoro (working directory ) ed inserendola quindi nel repo è detta nor-
malmente commit (in altri sistemi anche check-in), quella di estrarre una
versione dal repo nella directory di lavoro è chiamata di solito check-out.
In molti sistemi, le varie versioni si distinguono per un qualche numero
progressivo (tipicamente automatico) o per delle tag (etichette) specicate
dall'utente. git ha un approccio diverso: ogni versione è identicata da una
stringa di 40 caratteri che è un hash del commit (per cui sono anche sicuro
dell'integrità dei dati quando li estraggo). git permette anche di rmare il
commit con una chiave crittografata, per poter anche sapere con sicurezza
chi ha fatto il commit.
7
dei "sicuri progressi" che non c'è pericolo di perdere. Questa tranquillità si
trasforma poi anche in una maggiore velocità di lavoro!
Un eetto simile si ottiene anche usando dei sistemi automatici di "con-
trollo di corretto funzionamento" (testing ); che tra l'altro danno il meglio di
sé se usati in modo complementare ad un vcs.
4 Esempi d'uso
4.1 Primo
Vediamo ora un esempio d'uso di git attraverso le varie fasi di sviluppo di
un semplice programma Python:
8
7. aggiunta di una tag ;
8. modica, ripensamento e ritorno alla versione precedente;
ls -a
.
..
.git
cat exa/data.txt
zot 9
foo 123
bar 666
foo 40
foo 100
cat exa/account.py
# account.py
f = open("data.txt")
for r in f:
print r
9
4.1.4 Controllo della situazione
Vediamo ora qual'è la situazione 'secondo git', usando il comando status.
git status .
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
account.py
data.txt
nothing added to commit but untracked files present (use "git add" to track)
L'output di status indica che siamo sul branch (ramo) master, ossia
il ramo principale, nel commit iniziale (quello della creazione del repo ) e
che i due le che ho creato sono untracked ossia non gestiti da git. Inne
suggerisce di usare il comando add per aggiungerli all'area di stage ossia
all'insieme dei le pronti per il successivo commit. Siccome entrambi i le
sono in uno stato per me "corretto" (o di cui comunque voglio mantenere
memoria), seguo il consiglio.
Notare quindi che git ignora in linea di principio i le che non sono stati
esplicitamente aggiunti (con add) al repository. Ciò è sensato in quanto nella
directory di lavoro, ci sono sempre dei le "temporanei" o "non importanti"
di cui non si vuole tenere traccia. Questi le continuano però ad apparire
nell'output di status e di altri comandi. Se si desidera specicare esplicita-
mente quali le (e directory, anche ricorsivamente) si vogliono ignorare lo si
può fare creando un le 8.1. .gitignore
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
10
Ora eseguo il commit e ricontrollo con status.
git commit account.py data.txt -m "Esempio di accounting. Versione iniziale"
git status .
On branch master
nothing to commit, working directory clean
git log .
commit 5ea67613014666d9285107dda1796fcd86e371a0
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 14:36:45 2014 +0100
# account.py
d = dict()
f = open("data.txt")
for r in f:
n,v = r.split()
11
if not n in d:
d[n] = 0
d[n] += int(v)
print (d)
Vediamo cosa ottendo da status e come posso vedere le dierenze tra
la versione corrente e l'ultima di cui ho fatto commit (la più recente nel
repository)>
git status .
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: account.py
no changes added to commit (use "git add" and/or "git commit -a")
1. usare add per aggiungerlo allo stage (in vista di un futuro commit );
2. fare un commit diretto usando l'opzione -a che esegue il commit non
dei le staged ma di tutti quelli modicati;
Io non faccio nulla e invece uso il comando diff per vedere le dierenze
tra la versione corrente e quella del repository.
12
+d = dict()
+f = open("data.txt")
+for r in f:
+ n,v = r.split()
+ if not n in d:
+ d[n] = 0
+ d[n] += int(v)
+print (d)
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: account.py
git log .
commit c2aa5289e4098eccf300e89f543bef29b77a8031
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 17:46:44 2014 +0100
commit 5ea67613014666d9285107dda1796fcd86e371a0
13
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 14:36:45 2014 +0100
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
In eetti ora ho nella directory corrente la versione iniziale dei miei le,
ad esempio di account.py.
cat account.py
# account.py
f = open("data.txt")
for r in f:
print r
Adesso posso stamparla o fare ciò che mi pare e poi tornare ad usare la
"versione più recente" (master ) semplicemente con un altro checkout:
Notare che nel repository non rimane traccia di questa operazione, perché
non ha cambiato nulla nel repository stesso.
14
git log .
commit c2aa5289e4098eccf300e89f543bef29b77a8031
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 17:46:44 2014 +0100
commit 5ea67613014666d9285107dda1796fcd86e371a0
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 14:36:45 2014 +0100
git status .
On branch fix-spurious-newline
nothing to commit, working directory clean
# account.py
f = open("data.txt")
for r in f:
<<<<<<< HEAD
print r
# account.py
d = dict()
15
f = open("data.txt")
for r in f:
n,v = r.split()
if not n in d:
d[n] = 0
d[n] += int(v)
print (d)
=======
print r[:-1]
>>>>>>> fix-spurious-newline
git status .
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
no changes added to commit (use "git add" and/or "git commit -a")
Ora però, meglio tardi che mai, mi accorgo che il programma non funziona
"bene" in quanto non gestisce righe vuote (e nemmeno righe con un numero
di stringhe diverso da due e nemmeno il caso in cui la seconda non sia un
16
intero...). Quindi decido di fare un branch per il x. Vediamo se questa volta
sono più fortunato.
Posso fare il x sulla versione corrente, ma il baco è già presente nella sec-
onda versione (come posso scoprire facendo dei checkout o dei di o usando
git grep), quindi faccio un branch "basato" su quella.
# account.py
d = dict()
f = open("data.txt")
for r in f:
try:
n,v = r.split()
except ValueError:
continue
if not n in d:
d[n] = 0
d[n] += int(v)
print (d)
Ora add, commit, checkout master, merge...
Auto-merging account.py
Merge made by the 'recursive' strategy.
account.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
git log .
commit 8161b2483c1182c2f3332d051808fb8ee7a0b18b
Merge: 92b7520 45fc908
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 19:48:01 2014 +0100
17
Merge branch 'fix-split-error'
commit 45fc908179df3ad79f6c93de17458a3ae9e0f963
Author: Luca Manini <manini.luca@tiscali.it>
Date: Mon Nov 10 19:46:20 2014 +0100
cat account.py
# account.py
d = dict()
f = open("data.txt")
for r in f:
try:
n,v = r.split()
except ValueError:
continue
if not n in d:
d[n] = 0
d[n] += int(v)
f.close()
o = open("total.txt", "w")
for k,v in d.items():
o.write("%s -> %d\n" % (k, v))
o.close()
18
git commit . -m "New file a.txt"
git status # git trova la directory "clean"
git log --oneline # una riga dell'unico commit
19
git merge f-3 -m "Merging f-3 into master"
git log --oneline
mkdir -p fake-remote
cd fake-remote
20
4.3.2 Alice crea un le con tre commit e fa un push
Conguro nome e email di Alice (localmente in questo repo), in modo che
i suoi commit si distinguano da quelli di Bob anche se, dal punto di vista
dell'OS, sono eseguiti dallo stesso utente. Sarebbe forse meglio avere due
utenti separati ma ciò complicherebbe la scrittura di queste note.
Alice crea un singolo le di tre righe, aggiungendo una riga alla volta e
facendo sempre add e commit. Alla ne fa un push che, senza argomenti fa
il push del branch corrente verso il remote corrente.
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
a.txt
nothing added to commit but untracked files present (use "git add" to track)
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
21
create mode 100644 a.txt
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
nothing to commit, working directory clean
echo 2 >> a.txt; git add a.txt; git commit a.txt -m "Add line 2."
echo 3 >> a.txt; git add a.txt; git commit a.txt -m "Add line 3."
git push
To /home/manini/scuola/topics/git/exa/fake-remote/repo
* [new branch] master -> master
git status
On branch master
Initial commit
git fetch
From /home/manini/scuola/topics/git/exa/fake-remote/repo
* [new branch] master -> origin/master
git remote
origin
22
git branch -r
origin/master
commit d096fa0a1b556748b7050508a2f6b2237280f320
Author: Alice <alice@wonderland.net>
Date: Fri Mar 4 17:51:39 2016 +0100
Add line 3.
commit 7818e84ceea04f761c8979f2c1f1bafc49dedaf8
Author: Alice <alice@wonderland.net>
Date: Fri Mar 4 17:51:39 2016 +0100
Add line 2.
commit c2fbe2eaf15c4249169aeb61edf7ca80534636d1
Author: Alice <alice@wonderland.net>
Date: Fri Mar 4 17:51:39 2016 +0100
Create a.txt
git merge
echo 4 >> a.txt; git add a.txt; git commit a.txt -m "Add 4 to a.txt"
echo 5 >> a.txt; git add a.txt; git commit a.txt -m "Add 5 to a.txt"
echo 6 >> a.txt; git add a.txt; git commit a.txt -m "Add 6 to a.txt"
23
c24c367 Add 6 to a.txt
72507ed Add 5 to a.txt
e243742 Add 4 to a.txt
d096fa0 Add line 3.
7818e84 Add line 2.
c2fbe2e Create a.txt
5 Workow
git ore le funzionalità di base per il controllo del codice, ma non impone
nessun workow (usso, metodo, protocollo di lavoro). Una possibile orga-
nizzazione si basa sull'esistenza contemporanea di vari "tipi" di branch.
Come al solito, nella scelta e nella gestione del workow si deve trovare un
equilibrio tra la essibilità e complessità. Il workow è poi sicuramente
inuenzato dalla metodologia di sviluppo e di testing (ma questa è un'altra
storia).
24
6 github
Ho un account su github (prof-manini prof.manini.59@gmail.com switched
site me number).
Ho creato un progetto hello-world (come da tutorial), ho aggiunto un
le, fatto un branch, commit etc tutto dall'interfaccia web. Poi ho fatto un
clone locale, fatto delle modiche e poi ho cercato di fare un push. Non sono
riuscito e quindi sono passato a ssh.
Sul portatile di casa ho creato una chiave ssh (ss-genkey) con passphrase
(prof where number) e ho aggiunto la chiave all'account github.
Come suggerito su stackoverow, ho cambiato l'URL del remote con:
7.2 add
Add le contents to the index
add aggiunge uno o più le all'area di stage, dove risiedono i le che
saranno interessati dal prossimo commit. È importante notare che non
viene semplicemente aggiunto "il nome" del le, ma viene anche registrato
il suo "contenuto" corrente; e quindi se dopo una add il le viene modicato
e si desidera che queste modiche vengono inserite nel commit è necessario
ripetere il comando add !.
add può essere invocato in molti modi, passando come argomenti le o
directory ed ha anche molte opzioni. Ricordarsi che si può "sperimentare"
tranquillamente perché è sempre possibile rimuovere le dalla staging area
con il comando git rm --cached (XXX vero?).
È abbastanza frequente usare il comando git add . che aggiunge, ri-
corsivamente, tutti i le della directory corrente (anche nuovi).
25
7.3 commit
Record changes to the repository
commit "salva" lo stato corrente dell'area di stage nel repository. I con-
tenuti soggetti al commit sono specicati da:
Importante:
1. se subito dopo aver fatto un commit "ci si pente", si può cancellare
l'operazione con un reset;
Opzioni utili:
7.4 status
Show the working tree status
status mostra lo stato della working directory, elencando i le che:
1. sono diversi tra stage e working tree (probabili soggetti per un succes-
sivo add);
26
2. sono diversi tra stage e il commit "corrente" (probabili soggetti per un
successivo commit);
3. sono presenti nel working tree ma non nello stage (probabili soggetti
per un successivo add o rm o inclusione in .ignore).
7.5 log
Show commit logs
log mostra il log dei commit. Ha un sacco di opzioni per limitare l'output
e per scegliere il relativo formato.
La forma più frequente è sicuramente git log --oneline che mostra
una singola riga per ciascun commit con il nome del commit e la prima riga
del messaggio di commit.
Un'altra forma frequente è git log --oneline --branch --graph che
mostra anche i vari branch in forma di "grafo" (ASCII art).
Oppure git log --stat che mostra anche la lista dei le "cambiati" con
il conteggio delle righe cambiate.
7.6 checkout
Switch branches or restore working tree les
Il comando checkout può essere eseguito passando come argomento un
branch, un commit o una tag.
Il caso più normale è il branch ed ha due eetti "separati". Prima di
tutto la directory corrente viene modicata in modo che il suo contenuto
corrisponda alla situazione "registrata" nel commit. Inoltre il branch speci-
cato diventa il branch corrente. Si usa spesso nella forma git branch -b
<branch>, equivalente a git branch <branch>; git checkout <branch>.
Il checkout di un commit è pensato solo per ottenere i le per, ad esempio,
stamparli o spedirli a qualcuno, non per continuare a lavorarci. Infatti lascia
il working tree in uno stato un po' "pericoloso" detto detached head perché
associato ad un commit senza nome (anche se poi lo si può aggiungere più
tardi con un branch, ma è un po'un casino).
7.7 reset
Reset current HEAD to the specied state
reset permette di "ritornare" ad uno stato specico, denito da un com-
mit. È utile, per esempio, se per sbagli abbiamo fatto un commit nel branch
sbagliato (per esempio master), perché ci siamo dimenticati di fare un check-
out per cambiare branch (per esempio a x).
reset ha tre "livelli" di reset che posso scegliere da opzione. Tutti e tre
portano HEAD allo stesso commit (precedente) ma si dierenziano per lo
27
stato della working directory e della cache. Io in terpreto le dierenze come
misura di quanto "indietro" vado nella storia.
1. soft: i le di cui ho fatto commit vengono "tirati indietro" solo no
alla cache, quindi in un certo senso ho fatto l'undo solo del commit;
2. mixed (il default): i le vengono "tirati indietro" no alla working
directory, quindi ho fatto l'undo anche dell'add ;
3. hard: in questo caso tutti i le tracked vengono ripristinati allo stato
che avevano prima del commit (quindi perdo denitivamente tutte le
modiche) mentre eventuali nuovi le di cui non avevo nemmeno fatto
add vengono lasciati in pace.
7.8 revert
Revert some existing commits
revert, un po' come reset, permette di "tornare indietro" dopo aver fatto
qualche "errore". reset però lo fa "modicando la storia" e quindi non è
proprio il caso di usarlo se ho già fatto dei push. revert invece crea dei nuovi
commit facendo delle modiche che "annullano" quelle fatte dai commit
"sbagliati". In questo modo la storia precedente rimane invariata e posso
fare tranquillamente push.
7.9 branch
List, create, or delete branches
Il comando branch senza parametri mostra la lista dei branch, segnando
con un asterisco il branch corrente. Un primo argomento indica un nuovo
branch ed un secondo argomento il branch "padre" (default quello corrente).
Ricordarsi che non rende il nuovo branch corrente (per questo serve un check-
out).
Con l'opzione -d cancella un branch.
7.10 merge
Join two or more development histories together
Il comando merge serve ad "incorporare" i commit di uno o più branch
in un unico, tipicamente nuovo, commit. La tipica sequenza git checkout
master; git merge feature serve ad incorporare nel branch master i com-
mit del branch feature. merge senza argomenti funziona solo se il branch
corrente ha un "remote branch" (tipico il caso in cui il repo locale sia stato
creato clonando un repo remoto).
Ci sono vari "tipi" di merge :
28
1. Il più semplice è il fast forward che si realizza quando il branch corrente
(es: master) è "la base" dell'altro (es: feature), per cui il merge è
banale: basta spostare l'etichetta master al commit a cui punta feature
per cui non viene creato un nuovo commit. Ciò accade quando dopo il
branch di feature non sono stati fatti commit su master.
2. Il più generale è il three way merge in cui c'è stata una eettiva bi-
forcazione ed entrambi i branch hanno dei commit propri a valle del
parent comune. In questo caso git prova a creare un nuovo commit
che integri le "modiche" di entrambi i branch combianando il commit
di biforcazione (il parent comune) e i due commit "di testa" dei due
branch (da cui il nome three way ). Se non vi riesce per ciascun le con
conitti crea una versione "combinata": l'utente dovrà poi "mettere a
posto" il contenuto dei le, e fare add e commit normalmente.
7.11 rebase
Forward-port local commits to the updated upstream head
Il comando rebase permette di "trasformare" una situazione che richiederebbe
un merge three way in una in cui è suciente un fast forward. Supponiamo di
avere la solita biforcazione master feature. Se da feature eseguo git rebase,
git costruisce, a partire dai commit di feature a valle della biforcazione, una
serie di commit "equivalenti" che partono però dalla testa di master. In
questo modo un successivo commit è un fast forward !
Ciò ha il vantaggio di rendere di nuovo lineare la storia di master, ma
d'altra parte fa "dimenticare" che c'è stato un branch.
7.12 fetch
git-fetch - Download objects and refs from another repository
Il caso forse più frequente è quello in cui il repository corrente è stato
creato clonando un altro repository che a questo punto è un suo remote iden-
ticato dal riferimento origin/master. In questo caso il comando git fetch
scarica dal repo remoto i commit (e i relativi riferimenti) necessari ad "ag-
giornare" il repo locale, commit si possono eventualmente successivamente
integrare con un merge.
7.13 pull
Fetch from and integrate with another repository or a local branch
Un pull è equivalente a fetch seguito da merge.
29
7.14 push
Update remote refs along with associated objects
In breve ... è come fare pull dall'altro lato!
7.15 clean
Cancella dalla working directory tutti gli oggetti che non sono tracked. Con
l'opzione -d cancella (ricorsivamente) le directory, con l'opzione -f cancella
i le.
Con l'opzione -x cancella anche i le ignored, con l'opzione -X (maius-
cola) cancella solo i le ignored.
7.16 reog
8 TODO Varie
8.1 .gitignore
30