2017-06-20 00:15:44 +02:00
# Git crash course
2018-06-24 13:25:22 +02:00
## Davide Alberani <da@erlug.linux.it> 2017-2018
2017-06-20 00:15:44 +02:00
< br / >
Non-corso per non prendere a martellate il monitor quando usate Git.
2017-09-10 21:28:31 +02:00
< br / >
2017-08-20 09:05:22 +02:00
< br / >
2017-09-07 13:54:01 +02:00
2017-08-20 09:05:22 +02:00
**git clone https://git.lattuga.net/alberanid/git-crash-course.git**
2017-06-20 00:15:44 +02:00
< br / >
< br / >
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
2017-08-19 14:47:32 +02:00
A chi si trova a volere/dovere usare Git in un piccolo team, ed è ancora alle prime armi.
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
## Struttura del corso
2017-08-20 08:59:36 +02:00
### Parte 1
2017-07-30 22:26:31 +02:00
2017-08-20 08:59:36 +02:00
Le basi per lavorare in locale e con repository remoti.
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
### Parte 2
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
Un workflow per lo sviluppo collaborativo, da applicare senza porsi troppe domande.
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
### Parte 3
2017-07-29 16:32:24 +02:00
Una serie di strumenti avanzati, per chi ci ha preso gusto e vuole approfondire lo strumento.
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
## Cosa verrà trattato
2017-09-14 22:54:28 +02:00
* i comandi essenziali da riga di comando. Noti come **porcelain** ("ceramica"), sono comandi user-friendly di alto livello, contrapposti agli elementi *plumbing* ("tubature") di basso livello
2017-09-14 21:35:56 +02:00
2017-06-20 00:15:44 +02:00
* come gestire i branch
2017-09-14 21:35:56 +02:00
2017-06-20 00:15:44 +02:00
* le basi per lavorare con repository remoti
2017-09-14 21:35:56 +02:00
2017-08-20 08:59:36 +02:00
* un workflow per collaborare con altri sviluppatori
2017-06-20 00:15:44 +02:00
-----
## Cosa NON verrà trattato
2017-09-14 22:54:28 +02:00
* gli internals (**plumbing**) di Git
2017-09-11 19:15:51 +02:00
* *Github* (sorry, è solo un servizio di hosting)
2017-06-20 19:25:13 +02:00
* le GUI
2017-06-20 00:15:44 +02:00
* amministrazione di un repository remoto
2017-09-14 21:35:56 +02:00
* flame wars (discussioni) sui tipi di workflow
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
## Cosa è Git
Un sistema di controllo versione distribuito.
2018-06-24 13:25:22 +02:00
Serve per tener traccia dei cambiamenti al proprio codice e per facilitare lo sviluppo condiviso. Va ricordato che Git è nato soprattutto per aiutare chi deve integrare il codice altrui, e questo si riflette sulla sua logica.
2017-06-21 14:14:07 +02:00
2017-06-20 00:15:44 +02:00
< br / >
2017-09-10 21:25:06 +02:00
Il resto [lo spiega meglio Wikipedia ](https://it.wikipedia.org/wiki/Git%5F%28software%29 ).
2017-06-20 00:15:44 +02:00
-----
## Cosa NON è Git
* non è Subversion o CVS
* non è un sistema di backup
2017-08-23 17:57:12 +02:00
* non è un sistema per [fare deploy ](https://grimoire.ca/git/stop-using-git-pull-to-deploy ) (o magari sì, ma rifletteteci)
2017-06-20 00:15:44 +02:00
-----
## Si dice in giro
*Git non avrà segreti per voi, una volta compreso...*
2017-06-20 18:01:31 +02:00
* ...il data model (objects, blobs, trees, commits, refs, tags, ...)
2017-06-20 00:15:44 +02:00
* ...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
< br / >
### 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/...
2017-08-23 17:57:12 +02:00
* **HEAD**: il punto a cui sarà collegato il prossimo commit (di norma, il branch corrente)
2017-08-20 11:05:47 +02:00
2017-09-11 22:27:07 +02:00
* **refs**: nome collettivo per riferirsi ad HEAD, branches, tags
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-09-17 22:45:48 +02:00
## Le basi: prepariamo l'ambiente con git config
2017-06-20 00:15:44 +02:00
$ git config --global user.name "Davide Alberani"
$ git config --global user.email da@erlug.linux.it
$ git config --global color.ui auto
2017-09-18 20:25:51 +02:00
Le configurazioni vengono registrate nei files (in ordine di lettura: i successivi sovrascrivono le impostazioni dei precedenti):
* **/etc/git/config**: opzioni globali, valide per tutti gli utenti
* **~/.gitconfig** oppure ** ~/.config/git/config**: opzioni valide per l'utente corrente
* **.git/config** nel repository corrente: opzioni locali valide solo per il repository corrente
2017-09-17 22:45:48 +02:00
2017-09-18 20:25:51 +02:00
< br / >
2017-06-20 00:15:44 +02:00
### Bonus track
2018-06-24 13:25:22 +02:00
* cercare un esempio di ~/.gitconfig avanzato, con qualche alias per i comandi principali, come [questo ](https://github.com/alberanid/git-config/blob/master/gitconfig ) o [quest'altro ](https://gist.github.com/pksunkara/988716 )
2017-09-17 22:45:48 +02:00
-----
2017-09-18 20:25:51 +02:00
### Le basi: alcune opzioni di configurazione
2017-09-17 22:45:48 +02:00
2017-09-18 20:25:51 +02:00
Alias comuni:
2017-09-17 23:16:20 +02:00
2017-09-18 20:25:51 +02:00
$ git config --global alias.st status
$ git config --global alias.br branch
$ git config --global alias.co checkout
2017-09-17 23:16:20 +02:00
2017-09-18 20:25:51 +02:00
Colori:
2017-09-17 23:16:20 +02:00
$ git config --global color.branch.current "yellow bold"
$ git config --global color.branch.local "green bold"
$ git config --global color.branch.remote "cyan bold"
2017-09-18 20:25:51 +02:00
$ git config --global color.status.added "green bold"
2017-09-17 23:16:20 +02:00
2017-09-18 20:25:51 +02:00
Utente di Github:
2017-09-17 23:16:20 +02:00
2017-09-17 23:20:47 +02:00
$ git config --global github.username [nome]
2017-08-20 08:59:36 +02:00
---
2017-07-29 16:32:24 +02:00
## Parte 1
2017-08-20 08:59:36 +02:00
In cui forniamo le basi per lavorare in locale e con repository remoti.
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
---
2017-06-20 00:15:44 +02:00
## Le basi: creare un repository
Creare un nuovo repository partendo da una directory (vuota o meno):
$ git init
Clonare un repository remoto esistente:
2017-08-17 23:33:13 +02:00
$ git clone https://git.lattuga.net/user/repo.git
2017-06-20 00:15:44 +02:00
-----
2017-07-30 22:26:31 +02:00
## creare un repository: cosa è successo?
2017-06-20 00:15:44 +02:00
2017-08-24 20:25:53 +02:00
È stata creata la directory ** .git** (il **repository** ); se abbiamo fatto un clone, è stato aggiunto il riferimento al remote "*origin*".
2017-06-20 00:15:44 +02:00
< br / >
### Bonus track
2017-09-19 19:26:11 +02:00
* i repository remoti, in cui non si lavora direttamente ma ricevono solo comandi push e pull, vengono di norma creati con ** --bare** e non hanno working directory. Sono repo usati per **condividere** . Gli sviluppatori clonano il *shared bare repo* , fanno modifiche locali nelle loro working repo e fanno push nel *shared bare repo* per rendere le modifiche disponibili agli altri. Siccome nessuno edita direttamente nel *shared bare repo* , non serve avere un working tree. Anzi, questo potrebbe essere causa di conflitto.
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-08-13 20:22:43 +02:00
<!-- .slide: class="two - cols" -->
2017-06-20 00:15:44 +02:00
## Le basi: status
2017-09-10 21:25:06 +02:00
Vedere lo stato del sistema (usatelo spesso! un utile [cheatsheet ](https://ndpsoftware.com/git-cheatsheet.html )):
2017-06-20 00:15:44 +02:00
$ git status [-s]
< br / >
### 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
2017-08-13 20:22:43 +02:00
< img style = "width:300px" src = "images/file-states.png" data-action = "zoom" >
2017-07-30 22:26:31 +02:00
2017-06-20 00:15:44 +02:00
---
## Le basi: add e commit
Modifichiamo un file ed aggiungiamolo alla staging area:
$ git add prova.txt
Committiamolo:
$ git commit [-m "messaggio di commit"]
2017-07-30 22:26:31 +02:00
Verifichiamo quanto accaduto:
$ git log
2017-06-20 00:15:44 +02:00
-----
2017-08-24 20:25:53 +02:00
## add e commit: cosa è successo?
2017-06-20 00:15:44 +02:00
2017-08-23 17:57:12 +02:00
Abbiamo aggiunto un file alla staging area, per poi salvare uno snapshot del nostro lavoro. Se - come normalmente accade - siamo in un branch, questo punta al nuovo commit (HEAD continua a puntare al branch, e di conseguenza anch'essa al nuovo commit).
2017-06-20 00:15:44 +02:00
< br / >
### Bonus track
2017-08-24 20:25:53 +02:00
* indovinate cosa fanno **git rm** e **git mv**
2017-09-13 20:43:58 +02:00
* [committate spesso ](https://sethrobertson.github.io/GitBestPractices/ )
2017-08-25 18:55:29 +02:00
* come scrivere un messaggio di commit [che non susciti sgomento ](https://chris.beams.io/posts/git-commit/ )? Issue, titolo breve, descrizione estesa
2017-09-10 21:25:06 +02:00
* non salva directory vuote; se servono, aggiungete un file *.gitkeep* (è solo una convenzione)
* creare un file .gitignore per ignorare certi file
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
## Cosa sono i commit
2017-08-23 17:57:12 +02:00
Sono uno snapshot dell'intero stato del sistema in un dato momento, **identificati da un hash** (e.g.: *6d7696a8b894c8ef039d6fd2ecdc514a2efe16b5* ).
2017-06-20 00:15:44 +02:00
I commit hash sono generati partendo da: messaggio, committer, author, dates, tree, parent hash.
< br / >
### Bonus track
2017-09-10 21:25:06 +02:00
* è possibile abbreviare gli hash, purché rimangano univoci (e.g. *6d769* )
* per dettagli, vedere [anatomy of a Git commit ](https://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html ) e [Git Internals ](https://git-scm.com/book/it/v2/Git-Internals-Git-References )
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
## Le basi: la history
2017-06-20 18:01:31 +02:00
$ git log [--stat] [--patch] [--graph] [--decorate] [--color] [-2]
2017-06-20 00:15:44 +02:00
2017-09-10 21:25:06 +02:00
Rappresenta la storia dei commit dal punto corrente (o da/a qualsiasi punto indicato) fino al primo commit.
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
Si può limitare agli ultimi N commit con ** *-N***
2017-06-20 00:15:44 +02:00
< br / >
### Bonus track
2017-08-26 20:28:22 +02:00
* visualizzare solo i commit che hanno coinvolto un dato file: **git log -- file.txt**
* per avere informazioni su un singolo commit, si può anche usare **git show**
2017-09-13 20:43:58 +02:00
* per visualizzare quali commit hanno influenzato le singole righe di un file (e vedere chi le ha editate): **git blame file.txt**
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
## Le basi: diff
Modifichiamo un file, senza aggiungerlo alla staging area:
$ git diff
2017-09-10 21:25:06 +02:00
Per vedere quanto è stato posto in staging area (**e motivo per cui è utile usarla**):
2017-06-20 00:15:44 +02:00
$ git diff --staged
2017-09-10 21:25:06 +02:00
-----
## Le basi: tag
Un tag è un puntatore ad un commit:
$ git tag -a v1.0
< br / >
### 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.
2017-06-20 00:15:44 +02:00
---
## Aggiustare i danni
2017-09-16 13:06:43 +02:00
Modificare l'ultimo commit (cambiare il commit message o l'autore, oppure modificare un file - in questo caso va prima modificato nella working directory e fatto *git add* ):
2017-06-20 00:15:44 +02:00
$ 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
2017-09-10 21:25:06 +02:00
### Bonus track
* notate come il commit ID viene modificato, con *--amend* , motivo per cui è possibile usarlo solo sull'ultimo commit
* **git clean -f** per rimuovere tutti i file untracked
2017-06-20 18:01:31 +02:00
-----
## Aggiustare i danni: più forte
2017-06-20 00:15:44 +02:00
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 >
2017-08-19 14:47:32 +02:00
< br / >
2017-09-09 17:33:06 +02:00
### Bonus track
2017-08-19 14:47:32 +02:00
2017-09-10 21:25:06 +02:00
* maggiori [informazioni sul reset ](https://stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard )
* workflow [per risolvere problemi ](http://justinhileman.info/article/git-pretty/git-pretty.png )
2018-01-29 18:14:01 +01:00
* [qualche comando utile ](http://ohshitgit.com/ ) per risolvere i guai fatti
2017-08-19 14:47:32 +02:00
2017-06-20 00:15:44 +02:00
---
## 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.
-----
2017-06-20 18:01:31 +02:00
## Branches: creazione
2017-06-20 00:15:44 +02:00
Creare un branch:
$ git branch fix/bug-123
Visualizzare tutti i branch:
2017-09-09 17:33:06 +02:00
$ git branch [-a] [-v]
2017-06-20 00:15:44 +02:00
Cancellare un branch locale:
$ git branch -d [--force] fix/bug-123
2017-06-20 18:01:31 +02:00
-----
## Branches: spostiamoci
2017-06-20 00:15:44 +02:00
Spostarsi su un branch:
$ git checkout fix/bug-123
2017-09-18 20:36:40 +02:00
Creare e spostarsi in un singolo comando (può essere usato solo se il branch non esiste ancora):
2017-06-20 00:15:44 +02:00
$ git checkout -b fix/bug-123
2017-06-21 08:54:18 +02:00
< br / >
2017-07-30 22:26:31 +02:00
### Bonus track
2017-06-21 08:54:18 +02:00
2017-07-30 22:26:31 +02:00
* nello spostarsi, Git cerca di mantenere i cambiamenti presenti nella working directory e nella staging area
2017-06-21 08:54:18 +02:00
2017-06-20 00:15:44 +02:00
-----
## Branches: approfondiamo
2017-08-23 17:57:12 +02:00
* **master** è solamente un default (di norma si considera master "stabile")
2017-07-30 22:26:31 +02:00
2017-09-10 21:25:06 +02:00
* dare [nomi significativi ](http://www.guyroutledge.co.uk/blog/git-branch-naming-conventions/ ); usate prefissi come *bugfix/* , *fix/* , *improvement/* , *feature/* , *task/* ) e issue di riferimento
2017-08-23 17:57:12 +02:00
2017-09-13 20:43:58 +02:00
* prendete l'abitudine, **tutte** le volte che sviluppate un fix o una nuova feature, di farlo in un nuovo branch (che di norma partirà da *master* )
2017-08-23 17:57:12 +02:00
* possono essere logicamente suddivise: *feature* (o *topic* ), *release* , *integration* branches e così via
2017-07-30 22:26:31 +02:00
2017-06-20 00:15:44 +02:00
---
## Rimettere insieme i pezzi: merge
2017-09-18 13:01:37 +02:00
$ git checkout -b fix/bug-123
2017-06-20 00:15:44 +02:00
$ # editiamo nuovofile.txt
$ git add nuovofile.txt
$ git commit
2017-07-29 16:32:24 +02:00
2017-08-13 20:22:43 +02:00
< img style = "width:300px" src = "images/branch-commit.png" data-action = "zoom" >
2017-07-29 16:32:24 +02:00
2017-06-20 00:15:44 +02:00
$ git checkout master
$ git merge fix/bug-123
2017-08-13 20:22:43 +02:00
< img style = "width:300px" src = "images/branch-ff.png" data-action = "zoom" >
2017-07-29 16:32:24 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
## Merge: cosa è successo?
2017-06-20 00:15:44 +02:00
2017-09-10 21:25:06 +02:00
**fast-forward**!
2017-06-20 00:15:44 +02:00
2017-09-10 21:25:06 +02:00
master era più indietro rispetto a fix/bug-123, e quindi abbiamo semplicemente spostato il puntatore master. Non è stato neppure creato un nuovo commit.
2017-06-20 00:15:44 +02:00
Il comando commit ha le opzioni ** --ff-only** e ** --no-ff** per decidere come comportarsi.
-----
## Risoluzione dei conflitti
2017-09-18 20:36:40 +02:00
$ git branch fix/bug-123
$ git checkout fix/bug-123
2017-06-20 00:15:44 +02:00
$ # 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
2017-08-13 22:38:13 +02:00
< img style = "width:300px" src = "images/branch-conflict.png" data-action = "zoom" >
2017-08-20 08:59:36 +02:00
### Bonus track
2017-09-14 08:41:23 +02:00
* quali commit fanno parte del branch fix/bug-123 e quali di master?
2017-08-20 08:59:36 +02:00
2017-08-13 22:38:13 +02:00
-----
## Risoluzione dei conflitti
2017-08-01 22:48:31 +02:00
2017-06-20 00:15:44 +02:00
Mergiamo:
$ git merge fix/bug-123
$ # risolviamo i conflitti
$ git add file.txt
$ git commit
2017-08-13 22:38:13 +02:00
< img style = "width:300px" src = "images/branch-conflict-solved.png" data-action = "zoom" >
2017-07-29 16:32:24 +02:00
2017-08-17 23:33:13 +02:00
### Bonus track
2018-06-24 13:25:22 +02:00
* che succede al commit *C* se cancelliamo fix/bug-123?
2017-08-17 23:33:13 +02:00
2017-06-20 00:15:44 +02:00
-----
## Conflict files
2017-07-30 22:26:31 +02:00
Cercare sempre tutti i markers ** <<<<<<< **, ** =======**, ** >>>>>>>**
2017-06-20 00:15:44 +02:00
< br / >
### Bonus track
* potete usare **meld** come GUI per risolvere i conflitti
---
2017-07-30 22:26:31 +02:00
## Lavorare con repository remoti
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
$ git remote add origin https://git.lattuga.net/user/repo.git
2017-07-30 22:26:31 +02:00
$ git remote -v
2017-06-20 00:15:44 +02:00
< br / >
2017-09-09 17:33:06 +02:00
### Bonus track
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
* **origin** è solamente un default
2017-08-23 17:57:12 +02:00
* si può fare il checkout di un branch remoto con **remote/branch** (e.g.: *git checkout origin/fix/bug-123* )
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
## Fetch & pull
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Aggiornare il repository locale con i dati di un remoto:
2017-06-20 00:15:44 +02:00
2017-09-09 17:33:06 +02:00
$ git fetch --prune origin
2017-06-20 00:15:44 +02:00
2017-08-23 22:19:59 +02:00
Commit che divergono tra il master locale e quello remoto:
2017-06-20 00:15:44 +02:00
2017-08-19 14:47:32 +02:00
$ git log --left-right master...origin/master
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Scaricare gli aggiornamenti dal remoto e mergiare il branch corrente:
$ git pull origin
2017-06-20 00:15:44 +02:00
< br / >
2017-07-30 22:26:31 +02:00
### Bonus track
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
* **git pull** è identico a **git fetch ; git merge**
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-08-23 22:19:59 +02:00
## Branches locali e remoti
* **local branch**: un branch che avete solo in locale
* **remote branch**: un branch che esiste su un repository remoto
* **remote tracking branch**: la copia locale di un remote branch; aggiornabile con fetch, non è possibile lavorarci sopra direttamente
* **local tracking branch**: un branch locale su cui è possibile lavorare direttamente, che traccia un altro branch (di norma, un remote tracking branch)
2017-09-10 21:25:06 +02:00
* il local tracking di branch remoti viene effettuato in automatico, in base al nome del branch: se nel repository remoto esiste *origin/branch-1* , il comando *git checkout branch-1* crea un local tracking branch che traccia il remote tracking branch *origin/branch-1*
2017-08-23 22:19:59 +02:00
-----
2017-07-30 22:26:31 +02:00
## Push
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Aggiungere al repository remoto un branch locale:
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
$ git push --set-upstream origin local-branch-name
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
Inviare i cambiamenti locali ad un branch remoto:
2017-07-30 22:26:31 +02:00
2017-09-10 21:25:06 +02:00
$ git push [--tags] [origin [master]]
2017-06-20 00:15:44 +02:00
< br / >
2017-07-30 22:26:31 +02:00
### Bonus track
2017-06-20 00:15:44 +02:00
2017-09-18 14:56:41 +02:00
* git push di default non invia i tags, che vanno pushati separatamente aggiungendo --tags
2018-06-24 13:25:22 +02:00
* cancellare un branch remoto: **git push --delete origin branch-name**
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
## Parlando della history remota...
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Cosa da non fare **MAI** (salvo non ne siate davvero convinti): modificare una history che sia già stata pushata.
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
Questo perché se qualcuno sta lavorando sullo stesso branch remoto, le altre persone si troveranno con dei repository non coerenti.
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
---
2017-06-20 00:15:44 +02:00
2017-08-20 08:59:36 +02:00
## Parte 2
In cui forniamo un workflow precotto per chi non vuole porsi troppe domande, adatto allo sviluppo collaborativo.
---
## Quale workflow?
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Nello scegliere un workflow dovrete rispondere ad alcune domande, quali:
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
* chi parteciperà allo sviluppo? Vengono accettati contributi da esterni o solo da un gruppo ristretto?
* qual è il mio modello di rilascio del software? Ho versioni multiple da manutenere? A partire da quanti/quali branch verranno rilasciate le nuove versioni del mio software?
* chi si occuperà dell'integrazione? Gli sviluppatori stessi o una figura dedicata?
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-08-26 14:46:05 +02:00
<!-- .slide: class="align - left" -->
2017-07-30 22:26:31 +02:00
## Worflows: le alternative
I principali sono:
* centralized
* feature branch
* gitflow
* forking
* qualcosa tenuto insieme con gli elastici
Valide risorse:
* https://www.atlassian.com/git/tutorials/comparing-workflows
* https://guides.github.com/introduction/flow/
2017-06-20 00:15:44 +02:00
---
2017-07-30 22:26:31 +02:00
## Forking workflow
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Vediamo il **forking workflow** . Non perché sia intrinsecamente il migliore, ma perché quello più diffuso nello sviluppo su piattaforme come Github. Presupposti:
2017-08-23 17:57:12 +02:00
* esiste un repository ufficiale (che, dal punto di vista di un developer, chiameremo **upstream** ) di riferimento su cui solo gli autori principali possono scrivere
2017-08-17 23:33:13 +02:00
* ruolo di **project maintainer** : la persona che si occuperà di mergiare nel repository upstream
2017-07-30 22:26:31 +02:00
* ruolo di **developer** : chi sta sviluppando un fix o una nuova feature
2017-08-14 16:17:20 +02:00
* ciascun developer avrà un fork remoto del repository upstream ed una copia locale su cui lavorare
2017-07-30 22:26:31 +02:00
-----
2017-08-14 16:17:20 +02:00
## Forking workflow: maintainer setup
2017-07-30 22:26:31 +02:00
2017-08-14 16:17:20 +02:00
Il project maintainer ha creato il repository upstream remoto e il proprio clone locale.
2017-07-30 22:26:31 +02:00
2017-08-17 23:33:13 +02:00
$ git clone https://git.lattuga.net/maintainer/repo.git
2017-06-20 00:15:44 +02:00
2017-08-14 16:17:20 +02:00
< img style = "width:300px" src = "images/worflow-maintainer-clone.png" data-action = "zoom" >
2017-08-01 22:48:31 +02:00
2017-08-14 16:17:20 +02:00
-----
## Forking workflow: developer setup
Il developer ora:
* crea un **fork** remoto del repository upstream
< img style = "width:300px" src = "images/worflow-developer-fork.png" data-action = "zoom" >
2017-06-20 00:15:44 +02:00
2017-09-09 17:33:06 +02:00
### Bonus track
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
* un fork altro non è che un clone (--mirror) di un repository, sempre ospitato sul sito remoto
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-08-14 16:17:20 +02:00
## Forking workflow: developer setup
2017-09-07 13:54:01 +02:00
Developer fa un **clone** locale del proprio repository remoto. È una buona idea aggiungere un remote "**upstream**" che punti al repository del maintainer:
2017-08-17 23:33:13 +02:00
$ git clone https://git.lattuga.net/developer/repo.git
2017-09-13 20:43:58 +02:00
$ cd repo
2017-08-17 23:33:13 +02:00
$ git remote add upstream https://git.lattuga.net/maintainer/repo.git
2017-08-14 16:17:20 +02:00
< img style = "width:300px" src = "images/worflow-developer-clone.png" data-action = "zoom" >
-----
2017-07-30 22:26:31 +02:00
## Forking workflow: iniziamo lo sviluppo
2017-06-20 00:15:44 +02:00
2017-08-14 16:17:20 +02:00
Developer deve sviluppare un fix che andrà applicato sul branch master del repository upstream.
2017-06-20 00:15:44 +02:00
2017-08-26 10:17:24 +02:00
Prima di tutto è opportuno sincronizzare il proprio branch master con quello upstream, in modo da lavorare su codice recente:
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
$ git checkout master
2017-08-23 17:57:12 +02:00
$ git pull upstream master
2017-08-14 16:17:20 +02:00
2017-08-17 23:33:13 +02:00
< img style = "width:300px" src = "images/worflow-developer-pull-upstream.png" data-action = "zoom" >
2017-08-14 16:17:20 +02:00
-----
## Forking workflow: nuovo branch
2017-07-30 22:26:31 +02:00
$ git checkout -b fix/bug-123
2017-08-14 16:17:20 +02:00
< img style = "width:300px" src = "images/worflow-developer-branch.png" data-action = "zoom" >
2017-09-11 22:27:07 +02:00
### Bonus track
* **MAI** lavorare direttamente su *master* : perdereste la possibilità di sincronizzarlo di nuovo con *upstream* , in futuro
2017-08-14 16:17:20 +02:00
-----
## Forking workflow: lavoriamo
$ # introdurre il fix
2017-07-30 22:26:31 +02:00
$ git commit
$ git push --set-upstream origin fix/bug-123
2017-06-20 00:15:44 +02:00
2017-08-14 16:17:20 +02:00
< img style = "width:300px" src = "images/worflow-developer-push.png" data-action = "zoom" >
-----
## Forking workflow: pull request
2017-07-30 22:26:31 +02:00
Ora va sulla pagina web del proprio fork e crea una **pull request** .
2017-06-20 00:15:44 +02:00
2017-08-14 16:17:20 +02:00
< img style = "width:300px" src = "images/worflow-developer-pull-request.png" data-action = "zoom" >
2017-08-24 13:55:14 +02:00
### Bonus track
2017-09-11 22:27:07 +02:00
* ha senso fare un rebase su *upstream/master* del feature branch su cui stiamo lavorando, prima di creare la pull request (se avete già pushato, servirà un push --force) in modo che il vostro lavoro sia più vicino possibile allo stato attuale di upstream/master
2017-08-24 13:55:14 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
## Forking workflow: pull request
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Pull request **NON** è un concetto base di Git (non esattamente, almeno). È qualcosa che vi è stato costruito sopra per facilitare la collaborazione tra sviluppatori.
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
La pull request creata in precedenza dice: "propongo di applicare i commit del branch *developer:fix/bug-123* a *maintainer:master* "
2017-07-30 22:26:31 +02:00
Ora developer, project maintainer e altri possono discuterne.
2017-06-20 00:15:44 +02:00
2017-08-24 13:55:14 +02:00
Se dovesse essere necessario, developer o altri possono aggiungere altri commit semplicemente con un nuovo push.
2017-07-30 22:26:31 +02:00
-----
## Forking workflow: merging
2017-08-17 23:33:13 +02:00
Una volta soddisfatti, project maintainer potrà effettuare il merge del codice su *maintainer:master* .
2017-08-14 16:17:20 +02:00
2017-09-11 22:27:07 +02:00
**Se il merge non presenta conflitti**, lo farà direttamente dalla GUI web sul repository upstream.
2017-08-26 10:17:24 +02:00
2017-08-26 11:10:11 +02:00
Altrimenti dovrà aggiungere un remote che punti al repository di *developer* , fare il fetch di *developer:fix/bug-123* , effettuare il merge su master per poi farne il push sul repository upstream.
2017-07-30 22:26:31 +02:00
2017-08-23 23:48:09 +02:00
< img style = "width:300px;" src = "images/worflow-maintainer-local-fix.png" data-action = "zoom" >
-----
2017-08-26 14:29:15 +02:00
<!-- .slide: class="align - left" -->
2017-08-23 23:48:09 +02:00
## Forking workflow: bugia!
2017-09-11 22:27:07 +02:00
Github e amici non suggeriranno di aggiungere lo sviluppatore come remote, ma di fare direttamente il pull del suo topic branch. È sicuramente più pulito se ricevete molte pull request da tante persone differenti. Se invece il numero di contributori è basso (piccoli progetti, o in ambito lavorativo) ha senso aggiungere i loro repository come remotes.
2017-08-26 14:29:15 +02:00
Nel caso di Github, ad esempio:
2017-08-23 23:48:09 +02:00
2017-08-26 14:29:15 +02:00
1. git checkout -b developer/bug-123 master
1. git pull https://github.com/developer/repo.git fix/bug-123
1. git checkout master
1. git merge --no-ff developer/bug-123
1. git push origin master
2017-06-20 00:15:44 +02:00
2017-08-17 23:33:13 +02:00
-----
## Forking workflow: sunto setup maintainer
2017-08-19 14:47:32 +02:00
1. clone locale: **git clone https://git.lattuga.net/maintainer/repo.git**
2017-08-17 23:33:13 +02:00
-----
## Forking workflow: sunto setup developer
1. fork sul web
2017-08-19 14:47:32 +02:00
1. clone locale del fork: **git clone https://git.lattuga.net/developer/repo.git**
1. aggiunge un remote che punta al repository upstream: **git remote add upstream https://git.lattuga.net/maintainer/repo.git**
2017-08-17 23:33:13 +02:00
-----
## Forking workflow: sunto sviluppo developer
2017-08-23 17:57:12 +02:00
1. aggiorna il proprio master: **git checkout master ; git pull upstream master**
2017-08-19 14:47:32 +02:00
1. crea un branch su cui lavorare: **git checkout -b fix/bug-123**
2017-09-11 22:27:07 +02:00
1. lavora un sacco: **git commit**
1. opzionalmente, fa il rebase: **git rebase upstream/master**
2017-08-19 14:47:32 +02:00
1. invia le modifiche al proprio repository remoto: **git push --set-upstream origin fix/bug-123**
2017-08-17 23:33:13 +02:00
1. crea sul web una pull request
1. se serve, integra il lavoro semplicemente pushando altri commit fatti su fix/bug-123
-----
2017-08-26 11:10:11 +02:00
<!-- .slide: class="align - left" -->
2017-08-17 23:33:13 +02:00
## Forking workflow: sunto lavoro del maintainer
1. riceve una pull request e la valuta.
2017-08-26 11:10:11 +02:00
1. se mergiabile senza conflitti, lo fa via web.
*Altrimenti:*
1. se non lo ha già fatto, aggiunge un remote per il repository del developer: **git remote add developer https://git.lattuga.net/developer/repo.git**
1. crea una *local tracking branch* su cui lavorare: **git fetch developer fix/bug-123**
2017-08-26 14:46:05 +02:00
1. si sposta su master: **git checkout master**
1. effettua il merge risolvendo i conflitti: **git merge --no-ff fix/bug-123**
2017-08-19 14:47:32 +02:00
1. invia il master al proprio repository remoto: **git push origin master**
2017-08-17 23:33:13 +02:00
2017-06-20 00:15:44 +02:00
---
2017-08-20 08:59:36 +02:00
## Parte 3
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
In cui forniremo una serie di strumenti avanzati.
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
---
2017-06-20 00:15:44 +02:00
2017-08-01 22:48:31 +02:00
## Referenziare i commit
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Salire di 3 livelli, seguendo sempre il primo parent commit (in caso di merge):
2017-06-20 00:15:44 +02:00
2017-08-30 19:03:18 +02:00
$ git show -s HEAD~3
2017-07-30 22:26:31 +02:00
Salire di un livello, seguendo il secondo parent commit (in caso di merge):
2017-08-30 19:03:18 +02:00
$ git show -s HEAD^2
2017-06-20 00:15:44 +02:00
### Bonus track
2017-07-30 22:26:31 +02:00
* **detached HEAD**: ci siamo spostati su un commit che non è l'head di un branch
* questi operatori sono concatenabili: HEAD~~^2
2017-08-01 22:48:31 +02:00
-----
## Referenziare i commit: range
2017-08-19 14:47:32 +02:00
**Double dot range**. Usando *diff* mostra i cambiamenti tra "master" e "branch"; usando *log* mostra i commit raggiungibili da "branch" ma non da "master":
2017-08-01 22:48:31 +02:00
2017-08-19 14:47:32 +02:00
$ git diff master..branch
2017-08-01 22:48:31 +02:00
2017-08-19 14:47:32 +02:00
< br / >
2017-08-01 22:48:31 +02:00
2017-08-19 14:47:32 +02:00
**Triple dot range**. Usando *diff* mostra la differenza tra il punto di biforcazione tra "master" e "branch" e "branch" stesso; usando *log* mostra i commit raggiungibili da "master" o "branch", ma non da entrambi:
2017-08-01 22:48:31 +02:00
2017-08-19 14:47:32 +02:00
$ git log --left-right master...branch
2017-08-17 23:33:13 +02:00
2017-08-14 18:11:15 +02:00
-----
## Referenziare i commit: range
< img style = "width:300px" src = "images/range-log.png" data-action = "zoom" >
< img style = "width:300px" src = "images/range-diff.png" data-action = "zoom" >
2017-09-10 21:25:06 +02:00
Vedere anche [questa spiegazione ](https://stackoverflow.com/questions/7251477/what-are-the-differences-between-double-dot-and-triple-dot-in-git-dif )
2017-06-20 00:15:44 +02:00
---
2017-07-30 22:26:31 +02:00
## Rimettere insieme i pezzi: cherry-pick
2017-06-20 00:15:44 +02:00
2017-08-30 22:29:49 +02:00
$ git checkout master
2017-07-30 22:26:31 +02:00
$ git cherry-pick < commit >
$ # in caso di conflitti:
$ git cherry-pick --continue
2017-06-20 00:15:44 +02:00
2017-08-14 18:11:15 +02:00
< img style = "width:300px" src = "images/cherry-pick.png" data-action = "zoom" >
2017-08-01 22:48:31 +02:00
2017-08-19 14:47:32 +02:00
-----
2017-08-24 20:25:53 +02:00
## cherry-pick: cosa è successo?
2017-07-30 22:26:31 +02:00
Si sono prese le modifiche introdotte dai commit elencati, e sono state riapplicate sul branch corrente.
Sono stati creati dei nuovi commit.
< br / >
### 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.
2017-06-20 00:15:44 +02:00
---
2017-07-30 22:26:31 +02:00
## Rimettere insieme i pezzi: rebase
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Poniamoci nella stessa situazione divergente dell'esempio in cui abbiamo usato merge, e poi:
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
$ git checkout fix/bug-123
$ git rebase master
$ # risolviamo eventuali conflitti
$ git rebase --continue
2017-06-20 00:15:44 +02:00
2017-08-14 18:11:15 +02:00
< img style = "width:300px" src = "images/rebase.png" data-action = "zoom" >
2017-08-01 22:48:31 +02:00
2017-07-30 22:26:31 +02:00
### Cosa è successo?
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
Abbiamo preso tutti i commit di fix/bug-123 e li abbiamo ri-applicati su master, che nel mentre era andato avanti.
2017-08-23 22:48:25 +02:00
Tutti i commit specifici di fix/bug-123 sono cambiati. Volendo, ora si può fare un merge fast-forward in master.
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-06-20 00:15:44 +02:00
2017-08-24 20:25:53 +02:00
## Rebase: quando usarlo?
2017-07-30 22:26:31 +02:00
Quando dovete spostare più commit e/o per porvi nella condizione di fare un merge pulito. Questo può essere fatto dal developer prima di aprire una pull request per semplificare il lavoro al maintainer e/o dal maintainer stesso prima del merge, per ottenere una history lineare.
2017-06-20 00:15:44 +02:00
< br / >
2017-07-30 22:26:31 +02:00
### Quando NON usarlo?
2017-06-20 00:15:44 +02:00
2017-08-23 22:48:25 +02:00
Un rebase modifica i commit originali del branch: questo va evitato se quei commit sono già stati pushati ed altri sviluppatori li stanno usando come base per il proprio lavoro.
2017-06-20 00:15:44 +02:00
2017-07-30 22:26:31 +02:00
---
2017-07-29 16:32:24 +02:00
2017-08-14 18:11:15 +02:00
## Modificare la history: rebase interactive
2017-07-30 22:26:31 +02:00
Creiamo un nuovo branch e committiamo 2 o 3 modifiche. Poi:
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
$ git rebase -i master
2017-07-29 16:32:24 +02:00
2017-08-14 18:11:15 +02:00
< img style = "width:300px" src = "images/rebase-interactive.png" data-action = "zoom" >
2017-08-01 22:48:31 +02:00
2017-08-14 18:11:15 +02:00
-----
2017-07-29 16:32:24 +02:00
2017-08-24 20:25:53 +02:00
### Rebase interactive: cosa è successo?
2017-07-30 22:26:31 +02:00
Abbiamo accorpato, scartato o invertito l'ordine dei commit.
2017-07-29 16:32:24 +02:00
2017-08-24 20:25:53 +02:00
È particolarmente utile quando abbiamo finito di lavorare su un branch, e vogliamo semplificare la history accorpando molti commit in uno solo.
2017-08-14 18:11:15 +02:00
< br / >
2017-07-29 16:32:24 +02:00
### Bonus track
2017-07-30 22:26:31 +02:00
* l'opzione nucleare: **filter-branch** per creare script che riscrivono la history.
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
---
## Lavoro incompleto: committare a pezzi
2017-07-29 16:32:24 +02:00
2017-08-19 14:47:32 +02:00
Editiamo un file in vari punti, e poi aggiungiamolo alla staging area con --patch:
$ git add --patch
2017-07-30 22:26:31 +02:00
2017-08-14 18:11:15 +02:00
< br / >
### Quando usarlo?
Ad esempio quando non si vuole includere in un commit una riga di debug, che però si vuole mantenere nella working directory.
2017-08-20 08:59:36 +02:00
-----
2017-07-29 16:32:24 +02:00
2017-08-23 22:48:25 +02:00
## Creare e applicare patch
2017-09-10 21:25:06 +02:00
è possibile creare una patch usando il comando:
2017-08-23 22:48:25 +02:00
$ git format-patch [refs]
2017-09-10 21:25:06 +02:00
per poi applicarla con:
2017-08-23 22:48:25 +02:00
$ git apply patch-file.diff
-----
2017-07-29 16:32:24 +02:00
## Mettere il lavoro da parte: stash
2017-08-19 14:47:32 +02:00
Mettere da parte il lavoro nella working directory senza committare, e mostrare le modifiche stashed:
2017-07-29 16:32:24 +02:00
$ git stash
$ git stash list
2017-09-11 22:27:07 +02:00
Riapplicare una modifica messa in stash, ed eliminarne uno (**stash pop** combina i due comandi):
2017-07-29 16:32:24 +02:00
2017-09-11 22:27:07 +02:00
$ git stash apply stash@{0}
2017-07-29 16:32:24 +02:00
$ git stash drop stash@{0}
2017-08-14 18:11:15 +02:00
### Quando usarlo?
2017-07-29 16:32:24 +02:00
Ad esempio quando vogliamo passare ad un altro branch, accantonando le modifiche nella working directory.
2017-08-23 22:48:25 +02:00
-----
2017-07-29 16:32:24 +02:00
## Storico dei cambiamenti: reflog
La history mostra solo i commit inclusi in un branch.
2017-08-23 17:57:12 +02:00
Per vedere TUTTI gli spostamenti di HEAD:
2017-07-29 16:32:24 +02:00
2017-09-10 21:25:06 +02:00
$ git reflog [@{2 weeks ago}]
2017-07-29 16:32:24 +02:00
< br / >
### Quando usarlo?
* a volte è utile capire come ci siamo mossi tra i branch
* fondamentale per recuperare i **broken commits** (non referenziati da alcun branch)
2017-08-01 22:48:31 +02:00
---
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
## Idee sparse
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
* gestire file grandi: https://git-lfs.github.com/
* gestire file grandi (alternativa): https://git-annex.branchable.com/
* gestire la propria directory /etc: etckeeper
* gestire repository multipli: https://source.android.com/source/using-repo
2017-08-20 09:05:22 +02:00
* git repository manager: https://gogs.io/
* git repository manager: https://about.gitlab.com/
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
-----
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
## Pezzi mancanti
2017-07-29 16:32:24 +02:00
2017-09-13 20:43:58 +02:00
* [git submodule ](https://git-scm.com/docs/git-submodule ): gestire altri repository come sotto-moduli
* [git subtree ](https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/ ): inserire un repository in una sottodirectory
* [git bisect ](https://git-scm.com/docs/git-bisect ): cercare il commit in cui è stato introdotto un bug
* [git gui ](https://git-scm.com/docs/git-gui ) e [gitk ](https://git-scm.com/docs/gitk ): GUI per visualizzare commit e repository
* [tig ](https://jonas.github.io/tig/ ): interfaccia testuale
* [Gitgraph.js ](http://gitgraphjs.com/ ): creare grafi di commit e branch
2017-07-30 22:26:31 +02:00
---
2017-08-26 14:46:05 +02:00
<!-- .slide: class="align - left" -->
2017-08-25 18:36:30 +02:00
## Risorse per imparare
2017-07-30 22:26:31 +02:00
2017-08-01 22:48:31 +02:00
* Pro Git: https://git-scm.com/book/en/
2017-07-30 22:26:31 +02:00
* Reference: https://git-scm.com/docs
2017-08-01 22:48:31 +02:00
* Learn Git Branching: http://learngitbranching.js.org/
2017-07-30 22:26:31 +02:00
* Git ready: http://gitready.com/
* Git Cookbook: https://git.seveas.net/
* tutorial di Atlassian: https://www.atlassian.com/git/tutorials
2017-08-20 11:05:47 +02:00
* A visual Git reference: https://marklodato.github.io/visual-git-guide/index-en.html
2017-07-29 16:32:24 +02:00
< br / >
2017-07-30 22:26:31 +02:00
### Utilità
2017-07-29 16:32:24 +02:00
2017-07-30 22:26:31 +02:00
* bash prompt: https://github.com/magicmonty/bash-git-prompt
* Meld: http://meldmerge.org/
---
## The end
< br / >
2017-07-29 16:32:24 +02:00
2017-09-10 21:25:06 +02:00
**git clone https://git.lattuga.net/alberanid/git-crash-course.git**
2017-08-01 22:48:31 +02:00
< br / >
2017-09-10 21:27:49 +02:00
2017-09-10 21:25:06 +02:00
### Davide Alberani <da@erlug.linux.it>
2017-08-01 22:48:31 +02:00
< br / >
2017-09-10 21:25:06 +02:00
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License: http://creativecommons.org/licenses/by-sa/4.0/