Git Submodule
Introduzione ai sottomoduli Git: come aggiungerli, clonarli, aggiornarli, inviarli e rimuoverli con esempi pratici.
Un sottomodulo consente di incorporare un repository Git all'interno di un altro, mantenendo le rispettive cronologie completamente separate. Questa pagina spiega cos'è un sottomodulo, quando è lo strumento giusto da usare e il ciclo di vita completo dei comandi: aggiunta, clonazione, pull, aggiornamento, push e rimozione di un sottomodulo, oltre agli errori comuni che rendono i sottomoduli difficili da gestire.
Cos'è un Sottomodulo
Molto spesso, un repository di codice dipende da codice esterno proveniente da altri repository. È possibile copiare e incollare direttamente il codice esterno nel repository principale, oppure utilizzare il sistema di gestione dei pacchetti di un linguaggio. Tuttavia, entrambi i metodi hanno lo svantaggio di non tenere traccia delle modifiche al repository esterno.
Git consente di includere altri repository Git — detti sottomoduli — all'interno di un singolo repository. Un sottomodulo risiede in un percorso specifico nella directory di lavoro del repository padre ed è a tutti gli effetti un clone completo di un altro repository con la propria cronologia .git.
Il concetto fondamentale è che un sottomodulo è ancorato a un commit esatto, non a un branch o a un tag. Il repository padre memorizza solo il percorso del sottomodulo, l'URL e lo SHA del commit atteso. Questo è ciò che consente di dipendere da codice esterno in un punto noto e riproducibile nel tempo.
Due file tracciano questa relazione:
.gitmodules— un file tracciato nella radice del repository padre. Mappa il percorso di ciascun sottomodulo al relativo URL remoto (e facoltativamente un branch). Questo file viene committato e condiviso con tutti..git/confige la voce gitlink nell'albero — segnalibri locali per ogni clone che registrano il commit effettivamente estratto (il gitlink appare con modalità160000).
I sottomoduli supportano l'aggiunta, la sincronizzazione, l'aggiornamento e la clonazione, ma poiché il padre ricorda solo uno SHA di commit, aggiornare un sottomodulo è sempre un'azione deliberata in due fasi — mai automatica.
Quando Usare i Sottomoduli
Lavorare con i sottomoduli è complicato, quindi suggeriamo alcuni casi d'uso ottimali.
- Se il sottoprogetto cambia troppo rapidamente o le modifiche future potrebbero rompere l'API, blocca il codice a un commit specifico per sicurezza.
- Se un componente non viene aggiornato molto spesso e vuoi tenerlo tracciato come dipendenza vendor.
- Se rappresenti una parte del progetto a una terza parte e vuoi integrare il loro lavoro in un momento specifico (funziona solo quando gli aggiornamenti non sono troppo frequenti).
- Se il contesto tecnologico consente la creazione di pacchetti e la gestione formale delle dipendenze, dovresti usare i package manager invece dei sottomoduli.
- Se la tua codebase è enorme e non vuoi scaricarla ogni volta, usa i sottomoduli in modo che i collaboratori scarichino solo le parti di cui hanno bisogno.
Aggiungere un Sottomodulo
Prima, crea (o spostati in) il repository che conterrà il sottomodulo:
mkdir git-submodule-demo
cd git-submodule-demo/
git initInitialized empty Git repository in /Users/example/git-submodule-demo/.git/Aggiungi un sottomodulo con git submodule add, passando l'URL del repository da incorporare:
git submodule add https://somehost/example/textexampleCloning into '/Users/example/git-submodule-demo/textexample'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.Git clona immediatamente il repository textexample in una cartella con lo stesso nome e crea un file .gitmodules. Per posizionare il sottomodulo in un percorso diverso, aggiungilo come ultimo argomento, ad esempio git submodule add <url> vendor/textexample.
Ora controlla lo stato del repository con git status:
git statusOn branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: textexampleNota che textexample è in staging come singola voce, non come i singoli file al suo interno. Il repository padre tiene traccia solo del puntatore al commit del sottomodulo. Committa entrambi i file con git add e git commit:
git add .gitmodules textexample
git commit -m "Add textexample submodule"[master (root-commit) d5002d0] Add textexample submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 textexampleLa modalità 160000 è speciale: segna textexample come gitlink (un puntatore a un commit) piuttosto che come una directory normale.
Verificare lo Stato dei Sottomoduli
Prima di apportare modifiche, controlla dove si trova attualmente ciascun sottomodulo:
git submodule status a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 textexample (v1.2.0)Il carattere iniziale è significativo: uno spazio significa che il sottomodulo è al commit atteso, un + significa che è estratto a un commit diverso da quello registrato nel padre, e un - significa che non è ancora inizializzato.
Aggiornare i Sottomoduli
I membri del team devono aggiornare il codice del sottomodulo quando è stato modificato altrove. Non puoi affidarti solo a git pull, perché fare pull del repository padre cambia solo il commit del sottomodulo registrato — non tocca i file estratti all'interno del sottomodulo. Per estrarre il commit atteso dal padre, esegui:
git submodule updateSenza il flag --remote, questo comando estrae il commit registrato nel repository padre e non recupera le nuove modifiche upstream.
Per recuperare invece l'ultimo commit dal branch tracciato del sottomodulo (main per impostazione predefinita, o il branch impostato in .gitmodules), usa --remote:
git submodule update --remote textexampleQuesto recupera l'upstream del sottomodulo e lo fa avanzare. Il repository padre ora vede un nuovo puntatore al commit, quindi devi eseguire git add e committare il sottomodulo per registrare la modifica.
Per eseguire un aggiornamento su tutti i sottomoduli, inclusi quelli annidati, aggiungi --init --recursive:
git submodule update --init --recursiveSe il file .gitmodules cambia (ad esempio, l'URL del sottomodulo si sposta), esegui git submodule sync per copiare i nuovi URL nel tuo .git/config locale prima di aggiornare:
git submodule sync --recursiveClonare Git Submodule
Per clonare un progetto con sottomoduli, usa il comando git clone. Per impostazione predefinita, clona il repository padre ma lascia le directory dei sottomoduli vuote. Devi quindi eseguire git submodule init e git submodule update. Il primo aggiorna il .git/config locale con i mapping da .gitmodules, mentre il secondo recupera i dati del sottomodulo ed estrae il commit registrato.
Se hai clonato senza --recurse-submodules, popola i sottomoduli in seguito:
git clone /url/to/repo/with/submodules
git submodule init
git submodule updateIl collegamento git submodule update --init combina questi ultimi due comandi. Ancora meglio, clona tutto in un unico passaggio con --recurse-submodules:
git clone --recurse-submodules /url/to/repo/with/submodulesQuesto clona il padre e inizializza e aggiorna automaticamente ogni sottomodulo, così l'albero di lavoro è completo immediatamente.
Fare Pull del Codice del Sottomodulo
Quando esegui il pull di un repository padre che ha acquisito nuovi sottomoduli, quei sottomoduli arrivano non inizializzati. Prima, recupera l'ultimo stato del padre:
git pullSe vengono elencati nuovi sottomoduli, inizializzali e scaricali in un unico passaggio:
git submodule update --init --recursivegit submodule init da solo copia solo i mapping nel .git/config locale; non scarica alcun codice. Il passaggio update è quello che effettivamente recupera il sottomodulo ed estrae il commit registrato. Puoi fare in modo che git pull faccia questo automaticamente impostando:
git config submodule.recurse trueFare Push degli Aggiornamenti in un Sottomodulo
Un sottomodulo è un repository Git completo e indipendente, quindi effettui commit e push all'interno della sua directory esattamente come faresti altrove:
cd textexample
git checkout main
# ...edit files...
git commit -am "Fix typo in textexample"
git push
cd ..Dopo aver committato all'interno del sottomodulo, il repository padre punta ancora al vecchio commit. Eseguendo git status nel padre ora si mostra:
modified: textexample (new commits)Registra il nuovo puntatore mettendo in staging e committando il percorso del sottomodulo nel padre, poi esegui il push:
git add textexample
git commit -m "Bump textexample to latest"
git pushPer evitare di fare push del padre prima che i commit del suo sottomodulo esistano sul remoto (il che lascerebbe i colleghi con un puntatore non raggiungibile e non funzionante), fai push di tutto insieme:
git push --recurse-submodules=on-demandQuesto fa il push di eventuali commit non inviati del sottomodulo prima, poi del padre.
Rimuovere un Sottomodulo
Eliminare la cartella manualmente non è sufficiente — Git mantiene i propri dati di bookkeeping. Rimuovi un sottomodulo in modo pulito con:
git submodule deinit -f textexample
git rm textexample
git commit -m "Remove textexample submodule"deinit annulla la registrazione del sottomodulo e rimuove il suo albero di lavoro, git rm elimina il gitlink e la sua voce in .gitmodules, e il commit registra la rimozione.
Riepilogo
I sottomoduli sono un buon modo per mantenere i progetti in repository separati pur facendo riferimento ad essi come cartelle nella directory di lavoro di un altro repository. Il modello mentale da tenere a mente è semplice: il padre memorizza un puntatore al commit, ogni aggiornamento è deliberato, e --recurse-submodules ti salva da cloni parzialmente popolati. Per le dipendenze che cambiano frequentemente, un vero package manager è di solito la scelta migliore. Per approfondire i flussi di lavoro correlati, consulta git clone, git pull e git status.