Git è un sistema di controllo versione distribuito open source, creato da Linus Torvalds nel 2005 per lo sviluppo del kernel Linux. A differenza dei sistemi centralizzati come SVN, Git memorizza una copia completa del repository su ogni dispositivo dello sviluppatore, consentendo di lavorare senza una connessione costante al server. Secondo Git SCM, 2024, Git è utilizzato in oltre il 90% di tutti i progetti commerciali di sviluppo software.
Punti chiave
Git è un sistema di controllo versione distribuito (VCS) che tiene traccia delle modifiche nei file e consente a più sviluppatori di lavorare contemporaneamente sullo stesso progetto. A differenza dei sistemi centralizzati, in Git ogni sviluppatore ha una copia completa del repository, inclusa l’intera cronologia delle modifiche, rendendo il sistema resistente alla perdita di dati e senza la necessità di una connessione costante a un server centrale.
La storia di Git è iniziata nel 2005, quando Linus Torvalds creò un nuovo VCS dopo che BitKeeper aveva revocato la licenza gratuita per gli sviluppatori del kernel Linux. Gli obiettivi erano: velocità, semplicità dell’architettura, supporto per lo sviluppo non lineare attraverso il branching e distribuzione completa. In 3 mesi Torvalds scrisse il nucleo di Git, e in un anno il progetto divenne autogestito sotto la guida di Junio Hamano.
Secondo il sondaggio di Stack Overflow (2024), Git è utilizzato dal 93,9% degli sviluppatori professionisti, rendendolo il sistema di controllo versione dominante nel settore. Il concorrente più vicino — Subversion (SVN) — è utilizzato solo nel 5,2% dei progetti, principalmente in grandi ambienti aziendali con processi centralizzati.
Repository Git è una directory in cui Git tiene traccia delle modifiche a tutti i file. All’interno della directory c’è una cartella nascosta .git che memorizza tutti gli oggetti del sistema: commit, alberi, blob e riferimenti. Quando uno sviluppatore crea un commit, Git non copia i file per intero — crea un’istantanea e salva un riferimento ad essa.
Ogni commit contiene: un hash SHA-1 univoco (40 caratteri), un riferimento al commit precedente (parent), autore, data, messaggio del commit e un riferimento a un albero che descrive lo stato dei file al momento del commit. La catena di commit forma un grafo aciclico diretto in cui ogni commit punta a uno o più genitori.
# Inizializzazione del repository
git init my-project
cd my-project
# Creazione di un commit
echo "Ciao, Git" > README.md
git add README.md
git commit -m "Initial commit"
# Visualizzazione della cronologia
git log --oneline --graph --all
Git utilizza tre aree principali: working directory (file sul disco), staging area (indice dove vanno i file preparati) e repository (cronologia dei commit). Il comando git add sposta le modifiche dalla directory di lavoro allo staging, e git commit registra il contenuto dello staging nel repository. Questa separazione consente allo sviluppatore di assemblare un commit significativo da un insieme di modifiche senza dover confermare ogni modifica singolarmente.
I comandi Git di base coprono il 90% delle operazioni quotidiane di uno sviluppatore. Il comando git clone crea una copia locale di un repository remoto, git pull recupera le modifiche dal server e le unisce con il ramo corrente, e git push invia i commit locali al server. Questi tre comandi formano il ciclo principale del flusso di lavoro con Git.
Per visualizzare lo stato si usa git status — mostra quali file sono modificati, quali sono stati aggiunti allo staging e quali non sono tracciati. git diff mostra le modifiche specifiche nei file prima dell’aggiunta allo staging. Di seguito una tabella con i comandi più utilizzati:
| Comando | Azione | Esempio |
|---|---|---|
| git clone | Copia un repository remoto | git clone https://example.com/repo |
| git add | Aggiunge file allo staging | git add src/main.kt |
| git commit | Registra le modifiche nella cronologia | git commit -m “Correggere bug di login” |
| git push | Invia i commit al server | git push origin main |
| git pull | Recupera le modifiche dal server | git pull origin feature |
Per annullare le modifiche, Git offre diverse opzioni. git reset sposta il puntatore del ramo su un commit specificato e può resettare lo staging o la directory di lavoro. git revert crea un nuovo commit che annulla le modifiche del commit specificato — questo è un modo sicuro per annullare per i rami condivisi perché la cronologia non viene riscritta.
I rami in Git sono puntatori mobili leggeri verso un commit specifico. Creare un nuovo ramo non copia i file, ma crea semplicemente un nuovo puntatore, rendendo il branching praticamente istantaneo. Il ramo main (precedentemente master) è il ramo principale del progetto che contiene codice stabile pronto per il rilascio.
La pratica standard è utilizzare Git Flow o GitHub Flow. Git Flow utilizza i rami: main (codice di rilascio), develop (ramo di integrazione), feature/* (nuove funzionalità), release/* (preparazione dei rilasci) e hotfix/* (correzioni urgenti). GitHub Flow è più semplice: solo main e rami feature, e tutte le modifiche vengono consegnate tramite Pull Request.
# Creare e cambiare ramo
git branch feature-auth
git checkout feature-auth
# o con un singolo comando:
git checkout -b feature-auth
# Elenco dei rami
git branch --list
git branch -a # tutti i rami, inclusi quelli remoti
# Eliminare un ramo
git branch -d feature-auth
Una caratteristica importante del branching in Git è il cherry-pick: spostare un singolo commit da un ramo all’altro usando il comando git cherry-pick <hash>. Questo è utile quando è necessario trasferire una correzione di bug da un ramo feature a un rilascio senza unire l’intero ramo. Git supporta anche il rebase e il rebase interattivo (git rebase -i) per comprimere, riordinare e modificare i commit.
Merge (unione) crea un commit di merge speciale che ha due genitori. Questo commit registra il fatto dell’unione di due rami e preserva la cronologia completa — si può vedere dove e quando è avvenuta l’unione. Merge preserva la cronologia così come è stata creata, semplificando il controllo ma rendendo il grafo dei commit più complesso.
Rebase invece di creare un commit di merge, sposta i commit del ramo corrente sulla punta del ramo di destinazione. La cronologia diventa lineare — creando l’impressione che lo sviluppo sia stato sequenziale. Tuttavia, rebase riscrive la cronologia, modificando gli hash SHA-1 dei commit, rendendolo pericoloso per i rami condivisi a cui altri sviluppatori hanno accesso.
Raccomandazione: utilizzate merge per i rami pubblici dove la cronologia è visibile ad altri sviluppatori (feature → develop), e rebase per il lavoro locale quando è necessario applicare modifiche recenti da main al vostro ramo feature prima di creare una Pull Request. La regola è semplice: se un commit è già stato inviato al server — non fatene il rebase.
Conflitto di merge si verifica quando Git non può unire automaticamente le modifiche in un singolo file. Git contrassegna le sezioni in conflitto nel file con marcatori speciali: <<<<<<< (le nostre modifiche), ======= (separatore), >>>>>>> (le loro modifiche). Lo sviluppatore modifica manualmente il file, scegliendo l’opzione desiderata o combinandole entrambe, e completa l’unione con un commit.
Repository remoto (remote) è una copia di un repository Git situata su un server. GitHub, GitLab e Bitbucket sono le piattaforme più popolari per ospitare repository remoti. Forniscono un’interfaccia web per visualizzare il codice, gestire l’accesso, fare revisione del codice e integrarsi con sistemi CI/CD.
In Git, è possibile configurare più repository remoti per un progetto. Per impostazione predefinita, il remote principale si chiama origin. Il comando git remote add aggiunge un nuovo remote, git fetch recupera le modifiche senza unirle, e git pull è una scorciatoia per git fetch + git merge. Per lavorare con il codice tramite Pull Request, uno sviluppatore crea un fork del repository, lo clona, lavora in un ramo feature e invia una richiesta di unione al repository originale.
# Aggiungere un repository remoto
git remote add origin https://github.com/user/repo.git
# Visualizzare i repository remoti
git remote -v
# Inviare un ramo al server
git push -u origin feature-auth
# Recuperare le modifiche da un ramo remoto
git pull origin main
I repository remoti supportano il tagging per marcare le versioni di rilascio. I tag possono essere leggeri (solo un puntatore a un commit) o annotati (contengono metadati: autore, data, messaggio). I tag annotati sono raccomandati per le versioni di rilascio poiché contengono informazioni complete sulla versione e possono essere firmati con una chiave GPG per la verifica della paternità.
Git Worktree consente di lavorare contemporaneamente con più rami in directory diverse senza passare dall’uno all’altro. Il comando git worktree add ../feature-auth feature-auth crea una nuova directory di lavoro feature-auth dove è possibile scrivere codice senza cambiare ramo nella directory principale. Worktree è utile per correzioni rapide in un ramo release quando la directory principale è occupata con lo sviluppo a lungo termine.
Git Submodules è un meccanismo per includere un repository Git all’interno di un altro. Un submodule memorizza un riferimento a un commit fisso di un repository esterno, garantendo la riproducibilità della build. Il comando git submodule add https://github.com/example/lib.git aggiunge una libreria esterna come submodule. Quando si clona un progetto con submodules, è necessario eseguire git submodule update --init --recursive per scaricare tutte le dipendenze.
Domande frequenti
Git è un VCS distribuito con cronologia locale e possibilità di lavorare offline. SVN è un sistema centralizzato che richiede una connessione costante al server per qualsiasi operazione tranne la visualizzazione dei file.
Usa git revert HEAD per un annullamento sicuro (crea un nuovo commit). Se il commit non è stato ancora inviato al server, puoi usare git reset --soft HEAD~1.
.gitignore è un file che elenca i pattern di file e directory che Git deve ignorare. Viene utilizzato per escludere file temporanei, build e configurazioni IDE dal repository.
git fetch scarica le modifiche dal server ma non le unisce con il ramo corrente. git pull esegue fetch e immediatamente effettua un merge. Per avere controllo, usa fetch + revisione del diff, poi unisci manualmente.
Usa git commit --amend — questo comando apre un editor per modificare il messaggio del commit. Se il commit è già sul server, avrai bisogno di git push --force, che è pericoloso per i rami condivisi.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche