W3docs

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/config e 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 init
Initialized 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/textexample
Cloning 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 status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

	new file:   .gitmodules
	new file:   textexample

Nota 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 textexample

La 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 update

Senza 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 textexample

Questo 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 --recursive

Se 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 --recursive

Clonare 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 update

Il 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/submodules

Questo 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 pull

Se vengono elencati nuovi sottomoduli, inizializzali e scaricali in un unico passaggio:

git submodule update --init --recursive

git 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 true

Fare 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 push

Per 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-demand

Questo 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.

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.

Esercizio

Pratica
Quali sono gli aspetti chiave dell'utilizzo dei sottomoduli Git?
Quali sono gli aspetti chiave dell'utilizzo dei sottomoduli Git?
Was this page helpful?