12 KiB
Git crash course
Davide Alberani da@erlug.linux.it
Non-corso per non prendere a martellate il monitor quando usate Git.
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License: http://creativecommons.org/licenses/by-sa/4.0/
A chi è rivolto
Questo non è ancora chiaro...
Cosa verrà trattato
- i comandi essenziali (porcelain)
- come gestire i branch
- le basi per lavorare con repository remoti
Cosa NON verrà trattato
- gli internals (plumbing) di Git
- usi avanzati
- GitHub (sorry)
- amministrazione di un repository remoto
- flame wars sui workflow
Cosa è Git
Un sistema di controllo versione distribuito.
Serve per tener traccia dei cambiamenti al proprio codice e per facilitare lo sviluppo condiviso.
Il resto lo spiega meglio Wikipedia: https://it.wikipedia.org/wiki/Git%5F%28software%29
Cosa NON è Git
- non è Subversion o CVS
- non è un sistema di backup
Si dice in giro
Git non avrà segreti per voi, una volta compreso...
- ...il data model (objects, trees, commits, refs, tags, ...)
- ...il fatto che tutto è locale
- ...che i commit sono in realtà snapshot, e non delta rispetto allo stato precedente
- ...una qualche astrusa teoria a caso
Onestamente?
Tutto vero, ma la sua user interface è un mezzo disastro.
Le basi: definizioni
-
Working directory: i file su cui state lavorando
-
Staging area (o Index): dove mettiamo da parte le modifiche che finiranno nel prossimo commit
-
Commit: snapshot dello stato in un certo momento
-
(fare) Checkout: aggiornare i file nella working directory ad un dato branch/commit/...
Le basi: prepariamoci
$ git config --global user.name "Davide Alberani"
$ git config --global user.email da@erlug.linux.it
$ git config --global color.ui auto
Bonus track
- cercare un esempio di ~/.gitconfig avanzato
Le basi: creare un repository
Creare un nuovo repository partendo da una directory (vuota o meno):
$ git init
Clonare un repository remoto esistente:
$ git clone https://github.com/user/repo.git
Le basi: creare un repository
Cosa è successo?
È stata creata la directory .git (il repository); se abbiamo fatto un clone, sono stati aggiunti i riferimenti al remote alla configurazione.
Bonus track
- i repository remoti vengono creati con --bare e sono privi di working directory
- si può posizionare la directory .git in un altro path con --separate-git-dir
Le basi: status
Vedere lo stato del sistema:
$ git status [-s]
Stati dei file
-
Untracked: nuovi file nella working directory, non ancora aggiunti
-
Unmodified: file che non sono cambiati dal commit precedente
-
Modified: modificati nella working area e non ancora aggiunti alla staging area
-
Staged: nella staging area, pronti per il prossimo commit
Le basi: add e commit
Modifichiamo un file ed aggiungiamolo alla staging area:
$ git add prova.txt
Committiamolo:
$ git commit [-m "messaggio di commit"]
Cosa è successo?
Abbiamo aggiunto un file alla staging area, per poi salvare uno snapshot del nostro lavoro.
Bonus track
- git rm, git mv
- come scrivere un messaggio di commit che non susciti sgomento?
- le directory vuote non vengono salvate: .gitkeep / .gitignore
- git add --patch
Cosa sono i commit
Sono uno snapshot dell'intero stato del sistema in un dato momento, identificati da un hash.
I commit hash sono generati partendo da: messaggio, committer, author, dates, tree, parent hash.
Bonus track
- .gitignore per ignorare certi file
- è possibile abbreviarli, purché rimangano univoci.
- vedere https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html
- e anche https://git-scm.com/book/it/v2/Git-Internals-Git-References
Le basi: tag
Un tag è un puntatore ad un commit:
$ git tag -a v1.0
Bonus track
- esistono sia i tag lightweight che annotated. La differenza principale è che i primi sono solo dei puntatori, i secondi sono oggetti completi: hanno un author e possono essere firmati.
Le basi: la history
$ git log [--stat] [-p] [--graph] [--decorate] [--color] [-2]
Rappresenta la storia dei commit dal punto corrente (o da/a qualsiasi punto indicato).
Si può limitare agli ultimi N commit con -N
Bonus track
Visualizzare solo i commit che hanno coinvolto un dato file:
$ git log -- file.txt
Le basi: diff
Modifichiamo un file, senza aggiungerlo alla staging area:
$ git diff
Per vedere quanto è stato posto in staging area:
$ git diff --staged
Bonus track
è possibile creare e riapplicare una patch usando i comandi:
$ git format-patch [refs]
$ git apply patch-file.diff
Scopriamo chi incolpare!
Annotare un file con chi ha effettuato l'ultima modifica riga per riga:
$ git blame file.txt
Aggiustare i danni
Modificare l'ultimo commit (ad esempio per aggiungere un file, modificare il commit message o l'autore):
$ git commit --amend [--author="Name Surname <user@example.com>"]
Un file stato aggiunto per sbaglio alla staging area:
$ git reset HEAD -- file
Riportare un file modificato nell'ultimo stato committato/staged:
$ git checkout -- file
Ho fatto un casino nella working directory. Riportiamo tutto allo stato dell'ultimo commit:
$ git reset --hard HEAD
Voglio creare un nuovo commit che annulla le modifiche introdotte da un commit precedente:
$ git revert [-n] <commit>
Mettere il lavoro da parte: stash
Capita di dover mettere da parte il lavoro nella directory corrente senza voler committare:
$ git stash
Vedere la lista:
$ git stash list
Riapplicare una modifica messa in stash:
$ git stash pop [stash]
Eliminarne uno:
$ git stash drop stash@{0}
Storico dei cambiamenti: reflog
La history mostra solo i commit incluse in in branch.
Per vedere tutto ciò che è successo:
$ git reflog [--relative-date]
Quando usarlo?
- a volte è utile capire come ci siamo mossi tra i branch
- fondamentale per recuperare i broken commits (non referenziati da alcun branch)
Branches: cosa sono e perché usarli?
Sono puntatori mobili, spostati ad ogni nuovo commit.
Servono a separare diversi filoni di sviluppo e ad integrare i contributi di altri.
Branches: sperimentiamo!
Creare un branch:
$ git branch fix/bug-123
Visualizzare tutti i branch:
$ git branch -a
Cancellare un branch locale:
$ git branch -d [--force] fix/bug-123
Spostarsi su un branch:
$ git checkout fix/bug-123
Creare e spostarsi in un singolo comando:
$ git checkout -b fix/bug-123
Branches: approfondiamo
-
master è solamente un default
-
fate caso all'asterisco, è il branch corrente
-
dare nomi significativi; prefissi: bugfix/, fix/, improvement/, feature/, task/
-
aggiungere issue di riferimento
-
refs: nome collettivo per riferirsi ad HEAD, branches, tags
-
detached HEAD: ci siamo spostati su un commit che non è l'head di un branch
Spostarsi
Salire di 3 livelli, seguendo sempre il primo parent commit (in caso di merge):
$ git checkout HEAD~3
Salire di un livello, seguendo il secondo parent commit (in caso di merge):
$ git checkout HEAD^2
Bonus track
- cosa è HEAD: reference al branch (o commit) corrente
- questi operatori sono concatenabili: HEAD~~^2
- double/tripe dot ranges: git log master..branch; git log --left-right master...branch
Rimettere insieme i pezzi: merge
Partendo da master:
$ git branch -b fix/bug-123
$ # editiamo nuovofile.txt
$ git add nuovofile.txt
$ git commit
$ git checkout master
$ git merge fix/bug-123
Cosa è successo?
fast-forward! master era più indietro rispetto a fix/bug-123, e quindi abbiamo semplicemente spostato master.
Non è stato neppure creato un nuovo commit.
Il comando commit ha le opzioni --ff-only e --no-ff per decidere come comportarsi.
Risoluzione dei conflitti
Partendo da master:
$ git branch -b fix/bug-123
$ # editiamo file.txt
$ git add file.txt
$ git commit
$ git checkout master
$ # editiamo file.txt in maniera differente, sulle stesse righe
$ git add file.txt
$ git commit
Mergiamo:
$ git merge fix/bug-123
$ # risolviamo i conflitti
$ git add file.txt
$ git commit
Conflict files
Cercare sempre tutti i markers <<<<<<<, =======, >>>>>>>
Bonus track
- potete usare meld come GUI per risolvere i conflitti
Rimettere insieme i pezzi: cherry-pick
$ git cherry-pick <commit>
$ # in caso di conflitti:
$ git cherry-pick --continue
Cosa è successo?
Si sono prese le modifiche introdotte dai commit elencati, e sono state riapplicate sul branch corrente. Sono stati creati dei nuovi commit.
Quando usarlo?
Ad esempio per backportare un fix su diversi release branch, o se vi siete accorti che un certo commit era da fare su un altro branch.
Rimettere insieme i pezzi: rebase
Poniamoci nella stessa situazione divergente dell'esempio in cui abbiamo usato merge, e poi:
$ git checkout fix/bug-123
$ git rebase master
$ # risolviamo eventuali conflitti
$ git rebase --continue
Cosa è successo?
Abbiamo preso tutti i commit di fix/bug-123 e li abbiamo ri-applicati su master, che nel mentre era andato avanti.
Tutti i commit specifici di fix/bug-123 sono cambiati.
Rebase
Quando usarlo?
A spostare più commit e/o a porsi nella condizione di fare un merge pulito, mantenendo una history lineare.
Quando NON usarlo?
MAI MAI MAI rebasare dei commit che sono stati condivisi su altri repositori.
Modificare la history
Creiamo un nuovo branch e committiamo 2 o 3 modifiche. Poi:
$ git rebase -i master
Cosa è successo?
Abbiamo accorpato, scartato o invertito l'ordine dei commit.
Bonus track
- l'opzione nucleare: filter-branch per creare script che riscrivono la history.
Lavorare con repository remoti
$ git remote add origin https://github.com/user/repo.git
$ git remote -v
Bonus track
- origin è solamente un default
- associazione tra branch remoti e locali
Fetch & pull
Aggiornare il repository locale con i dati di un remoto:
$ git fetch --prune --tags origin
Differenze tra il master locale e quello remoto:
$ git log master..origin/master
Scaricare gli aggiornamenti dal remoto e mergiare il branch corrente:
$ git pull origin
Bonus track
- git pull è identico a git fetch ; git merge
Push
Aggiungere al repository remoto un branch locale:
$ git push --set-upstream origin local-branch-name
Aggiungiamo i cambiamenti locali ad un branch remoto:
$ git push --tags [origin [master]]
Bonus track
- git push di default non invia i tags
Parlando della history remota...
Cosa da non fare MAI (salvo non ne siate davvero convinti): pushare modifiche alla history.
Questo perché se qualcuno sta lavorando sullo stesso branch remoto, romperete tutto.
Idee sparse
Gestire file grandi:
Gestire la propria directory /etc:
- etckeeper
Pezzi mancanti
- git submodule
- git bisect
- git gui; gitk
- workflows!
Risorse
Per imparare
- Pro Git: https://git-scm.com/book/en/v2
- Reference: https://git-scm.com/docs
- Learn Git Brancing: http://learngitbranching.js.org/
- Git ready: http://gitready.com/
- Git Cookbook: https://git.seveas.net/
- tutorial di Atlassian: https://www.atlassian.com/git/tutorials
Utilità
- bash prompt: https://github.com/magicmonty/bash-git-prompt
- Meld: http://meldmerge.org/