Come posso specificare un ramo / tag quando aggiungo un sottomodulo Git?


756

Come git submodule add -bfunziona?

Dopo aver aggiunto un sottomodulo con un ramo specifico, un nuovo repository clonato (dopo git submodule update --init) sarà in corrispondenza di un commit specifico, non il ramo stesso ( git statussul sottomodulo mostra "Non attualmente su nessun ramo").

Non riesco a trovare alcuna informazione su .gitmoduleso .git/configsulla filiale del modulo o qualsiasi specifica impegnarsi, così come fa Git figura fuori?

Inoltre, è possibile specificare un tag anziché un ramo?

Sto usando la versione 1.6.5.2.


3
Se hai un sottomodulo esistente che non sta ancora monitorando un ramo , ma desideri che ora
segua

Risposte:


745

Nota: Git 1.8.2 ha aggiunto la possibilità di tenere traccia dei rami. Vedi alcune delle risposte di seguito.


È un po 'confuso abituarsi a questo, ma i sottomoduli non sono su un ramo. Sono, come dici tu, solo un puntatore a un particolare commit del repository del sottomodulo.

Ciò significa che quando qualcun altro controlla il repository o estrae il codice e esegue l'aggiornamento del sottomodulo, il sottomodulo viene estratto per quel particolare commit.

Questo è ottimo per un sottomodulo che non cambia spesso, perché tutti i membri del progetto possono avere il sottomodulo con lo stesso commit.

Se si desidera spostare il sottomodulo in un tag particolare:

cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push

Quindi, un altro sviluppatore che desidera che submodule_directory sia cambiato in quel tag, lo fa

git pull
git submodule update --init

git pullle modifiche che impegnano la loro directory di sottomodulo punta a. git submodule updatesi fonde effettivamente nel nuovo codice.


8
Questa è un'ottima spiegazione, grazie! E ovviamente, dopo aver letto la tua risposta, mi sono reso conto che il commit è stato salvato all'interno del sottomodulo stesso (sottomodule / .git / HEAD).
Ivan,

4
Questo non sembra funzionare su git 1.7.4.4. cd my_submodule; git checkout [ref in submodule's repositoryrese fatal: reference is not a tree: .... È come se gitfunzionasse solo sul repository padre.
James A. Rosen,

3
È utile utilizzare i sottomoduli git anche per progetti che vengono aggiornati spesso. Il kernel di Linux lo usa e non è poi così male

10
È git checkout v1.0un ramo o un tag?
Bernhard Döbler,

8
Considera un tag un alias leggibile da un commit. E un commit è un set di stato specifico per ciascun file. Un ramo è essenzialmente la stessa cosa, tranne che è possibile modificarlo.
deadbabykitten,

657

Vorrei aggiungere qui una risposta che è in realtà solo un conglomerato di altre risposte, ma penso che potrebbe essere più completo.

Sai di avere un sottomodulo Git quando hai queste due cose.

  1. La tua .gitmodulesvoce è così:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
    
  2. Hai un oggetto sottomodulo (chiamato SubmoduleTestRepo in questo esempio) nel tuo repository Git. GitHub li mostra come oggetti "sottomodulo". Oppure fai git submodule statusda una riga di comando. Gli oggetti sottomodulo Git sono tipi speciali di oggetti Git e contengono le informazioni SHA per un commit specifico.

    Ogni volta che fai un git submodule update, popolerà il tuo sottomodulo con il contenuto del commit. Sa dove trovare il commit a causa delle informazioni nel file .gitmodules.

    Ora, tutto -bciò che fa è aggiungere una riga nel tuo .gitmodulesfile. Quindi, seguendo lo stesso esempio, sarebbe simile al seguente:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
        branch = master
    

    Nota: in un .gitmodulesfile è supportato solo il nome del ramo , ma SHA e TAG non sono supportati! (invece di questo, il commit della diramazione di ciascun modulo può essere monitorato e aggiornato usando " git add .", per esempio come git add ./SubmoduleTestRepo, e non è necessario cambiare il .gitmodulesfile ogni volta)

    L'oggetto sottomodulo punta ancora a un commit specifico. L'unica cosa che l' -bopzione ti compra è la possibilità di aggiungere un --remoteflag al tuo aggiornamento secondo la risposta di Vogella:

    git submodule update --remote
    

    Invece di popolare il contenuto del sottomodulo al commit indicato dal sottomodulo, sostituisce quel commit con l'ultimo commit sul ramo master, ALLORA popola il sottomodulo con quel commit. Questo può essere fatto in due passaggi dalla risposta djacobs7. Poiché ora hai aggiornato il commit a cui punta l'oggetto sottomodulo, devi impegnare l'oggetto submodule modificato nel tuo repository Git.

    git submodule add -bnon è un modo magico di tenere tutto aggiornato con una filiale. Aggiunge semplicemente informazioni su un ramo nel .gitmodulesfile e ti dà la possibilità di aggiornare l'oggetto sottomodulo all'ultimo commit di un ramo specificato prima di popolarlo.


14
Questa risposta dovrebbe avere più voti positivi. Ho letto molti post per il giorno passato e questo chiarisce tutta la confusione. Provenendo dal mondo SVN e usando gli esterni - si vuole credere che il monitoraggio dei rami del sottomodulo git mantenga magicamente tutto aggiornato dal ramo - ma questo non è vero! Devi aggiornarli esplicitamente! Come accennato, è necessario eseguire il commit degli oggetti del sottomodulo modificati.
dtmland,

12
Questo tracciamento delle filiali funziona anche con i tag ? Invece di un ramo ho specificato un tag nel mio .gitmodulese dopo aver fatto $ git submodule update --init --remote TestModuleho ricevuto un errore dicendo fatal: Needed a single revisione Unable to find current origin/TestTag revision in submodule path 'TestModule'. Quando lo fai con un vero ramo funziona. Esiste un modo per specificare un tag .gitmodulessenza dover specificare il commit esatto?
Hhut,

5
Questo non sembra funzionare. Ho aggiornato l'hash .gitmodulese ho funzionato git submodule updatee non è successo niente?
CMCDragonkai

2
In qualche modo questo non funziona per me. Con un ID commit SHA, ricevo sempre un errore "Impossibile trovare la revisione corrente (ho ricontrollato il numero di revisione di HEAD e la sua corretta). Tuttavia, se uso master funziona, funziona.
infocloggato il

2
Anche l'immissione di un SHA nell'attributo branch non funziona per me. Anche questo utilizzo non è supportato dai documenti: git-scm.com/docs/gitmodules
Jakub Bochenski,

340

(Git 2.22, Q2 2019, ha introdotto git submodule set-branch --branch aBranch -- <submodule_path>)

Nota che se hai un sottomodulo esistente che non sta ancora monitorando un ramo , ( se hai git 1.8.2+ ):

  • Assicurati che il repository principale sappia che il suo sottomodulo ora traccia un ramo:

    cd /path/to/your/parent/repo
    git config -f .gitmodules submodule.<path>.branch <branch>
    
  • Assicurati che il tuo sottomodulo sia effettivamente al più tardi di quel ramo:

    cd path/to/your/submodule
    git checkout -b branch --track origin/branch
      # if the master branch already exist:
      git branch -u origin/master master
    

         (con "origine" come nome del repository remoto a monte da cui è stato clonato il sottomodulo. Verrà visualizzato
         un git remote -vinterno di quel sottomodulo. Di solito, è "origine")

  • Non dimenticare di registrare il nuovo stato del tuo sottomodulo nel repository principale:

    cd /path/to/your/parent/repo
    git add path/to/your/submodule
    git commit -m "Make submodule tracking a branch"
    
  • L'aggiornamento successivo per quel sottomodulo dovrà utilizzare l' --remoteopzione:

    # update your submodule
    # --remote will also fetch and ensure that
    # the latest commit from the branch is used
    git submodule update --remote
    
    # to avoid fetching use
    git submodule update --remote --no-fetch 
    

Nota che con Git 2.10+ (Q3 2016), puoi usare ' .' come nome di filiale:

Il nome del ramo è registrato come submodule.<name>.branchin .gitmodulesper update --remote.
Un valore speciale di .viene utilizzato per indicare che il nome del ramo nel sottomodulo deve avere lo stesso nome del ramo corrente nel repository corrente .

Ma, come commentato da LubosD

Con git checkout, se il nome del ramo da seguire è " .", ucciderà il tuo lavoro non confermato!
Usa git switchinvece.

Ciò significa che Git 2.23 (agosto 2019) o più.

Vedi " Confuso dagit checkout "


Se vuoi aggiornare tutti i tuoi sottomoduli seguendo un ramo:

    git submodule update --recursive --remote

Si noti che il risultato, per ogni sottomodulo aggiornato, sarà quasi sempre una TESTA distaccata , come osserva Dan Cameron nella sua risposta .

( Clintm nota nei commenti che, se si esegue git submodule update --remotee lo sha1 risultante è lo stesso del ramo su cui si trova attualmente il sottomodulo, non farà nulla e lascerà il sottomodulo ancora "su quel ramo" e non in stato head distaccato. )

Per assicurarsi che il ramo sia effettivamente estratto (e che non modifichi lo SHA1 della voce speciale che rappresenta il sottomodulo per il repository principale), suggerisce:

git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git switch $branch'

Ogni sottomodulo farà comunque riferimento allo stesso SHA1, ma se si effettuano nuovi commit, si sarà in grado di inviarli perché saranno indicati dal ramo che si desidera monitorare il sottomodulo.
Dopo tale push all'interno di un sottomodulo, non dimenticare di tornare al repository principale, aggiungere, eseguire il commit e inviare il nuovo SHA1 per quei sottomoduli modificati.

Si noti l'uso di $toplevel, raccomandato nei commenti di Alexander Pogrebnyak .
$toplevelè stato introdotto in git1.7.2 a maggio 2010: commit f030c96 .

contiene il percorso assoluto della directory di livello superiore (dove si .gitmodulestrova).

dtmlandaggiunge nei commenti :

Lo script foreach non eseguirà il checkout dei sottomoduli che non seguono un ramo.
Tuttavia, questo comando ti dà entrambi:

 git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git switch $branch' –

Lo stesso comando ma più facile da leggere:

git submodule foreach -q --recursive \
    'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; \
     [ "$branch" = "" ] && \
     git checkout master || git switch $branch' –

umläute perfeziona il comando di dtmland con una versione semplificata nei commenti :

git submodule foreach -q --recursive 'git switch $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

più righe:

git submodule foreach -q --recursive \
  'git switch \
  $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

Prima di Git 2.26 (Q1 2020), un recupero che viene detto di recuperare ricorsivamente gli aggiornamenti nei sottomoduli produce inevitabilmente risme di output e diventa difficile individuare i messaggi di errore.

Al comando è stato insegnato a enumerare i sottomoduli che presentavano errori alla fine dell'operazione .

Vedi commit 0222540 (16 gennaio 2020) di Emily Shaffer ( nasamuffin) .
(Unita da Junio ​​C Hamano - gitster- in commit b5c71cc , 05 feb 2020)

fetch: enfatizza l'errore durante il recupero del sottomodulo

Firmato-fuori-da: Emily Shaffer

Nei casi in cui un recupero del sottomodulo fallisce quando ci sono molti sottomoduli, l'errore del recupero del sottomodulo solitario non riuscito viene seppellito sotto attività sugli altri sottomoduli se più di un recupero ricade su fetch-by-oid.
Richiama un errore in ritardo, quindi l'utente è consapevole che qualcosa è andato storto e dove .

Perché fetch_finish()viene chiamato solo in modo sincrono dal run_processes_parallel,mutexing non è richiesto in giro submodules_with_errors.


1
Domanda: se ho la cartella subModule1 e desidero tracciare il ramo master, il comando risultante sarebbe simile al seguente: git config -f .gitmodules submodule.subModule1.branch master
BraveNewMath

1
La foreachscript non dipenderà dalla hard-coded <path>, se si sostituisce <path>con $toplevel/.
Alexander Pogrebnyak,

1
Lo foreachscript non verificherà i sottomoduli che non seguono un ramo. Tuttavia, questo comando fornisce entrambi:git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git checkout $branch'
dtmland il

2
ecco una versione semplificata della sceneggiatura di @ dtmland:git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
umläute,

1
Ohh! In realtà lo script foreach non è necessario. Dobbiamo eseguire l'aggiornamento del sottomodulo con l'opzione --merge o --rebase: git submodule update --remote --mergeo git submodule update --remote --rebase. Questi comandi eseguono il monitoraggio del ramo remoto.
GregTom,

206

Git 1.8.2 ha aggiunto la possibilità di tenere traccia dei rami.

# add submodule to track master branch
git submodule add -b branch_name URL_to_Git_repo optional_directory_rename

# update your submodule
git submodule update --remote 

Vedi anche sottomoduli Git


4
Questo vale anche per i tag?
ThorSummoner,

1
In che modo l'aggiunta del sottomodulo in tal modo si riflette sul .gitmodulesfile?
Eugene,

1
Grazie, ho appena usato le informazioni per aiutarmi a creare una cartella di sottomodulo sincronizzata con un sito Web gh-pages di GitHub: esempio completo su github.com/o2platform/fluentnode/issues/22
Dinis Cruz

4
Puoi bloccare un tag con git submodule add -b tags/<sometag> <url>cui puoi vedere la riga branch = tags/<sometag>in.gitmodules
KCD

9
@KCD Quale versione di git può farlo con i tag. Il mio non funziona?
CMCDragonkai

58

Un esempio di come utilizzo i sottomoduli Git.

  1. Crea un nuovo repository
  2. Quindi clonare un altro repository come sottomodulo
  3. Quindi abbiamo che il sottomodulo usa un tag chiamato V3.1.2
  4. E poi ci impegniamo.

E sembra un po 'così:

git init 
vi README
git add README
git commit 
git submodule add git://github.com/XXXXX/xxx.yyyy.git stm32_std_lib
git status

git submodule init
git submodule update

cd stm32_std_lib/
git reset --hard V3.1.2 
cd ..
git commit -a

git submodule status 

Forse aiuta (anche se uso un tag e non un ramo)?


4
È praticamente la stessa risposta di djacobs7, ma grazie comunque :)
Ivan,

1
Dovresti essere in grado di eseguire un cambiamento dopo il tuo git reset --hard V3.1.2? Ho appena ricevuto un "niente da impegnare" con una git statusdelle directory padre.
Nick Radford,

1
@Ivan: Potresti spiegare come questo è uguale alla risposta di djacobs7? Per quanto vedo, la sua risposta non include nemmeno il comando 'submodule add', invece il repository viene aggiunto direttamente, senza alcun collegamento al repository git originale del modulo. Almeno quando ho provato questo approccio non c'era alcun collegamento in .gitmodules.
Michel Müller,

La risposta di djacobs7 non include l'intera spiegazione a partire dall'aggiunta del sottomodulo. Presume che tu l'abbia già.
CodeMonkey,

non aggiunge semplicemente l'intero contenuto del sottomodulo come oggetti tracciati al repository principale?
user1312695

38

In base alla mia esperienza, il passaggio da una succursale all'altra nel superprogetto o in futuri checkout provocherà comunque HEAD distaccati dei sottomoduli, indipendentemente dal fatto che il sottomodulo sia correttamente aggiunto e tracciato (ad esempio, risposte @ djacobs7 e @Johnny Z).

E invece di estrarre manualmente il ramo corretto manualmente o tramite uno script git sottomodule foreach può essere usato.

Ciò controllerà il file di configurazione del sottomodulo per la proprietà branch e verificherà il ramo set.

git submodule foreach -q --recursive 'branch="$(git config -f <path>.gitmodules submodule.$name.branch)"; git checkout $branch'


Bello. +1. Ho incluso il tuo comando nella mia risposta .
VonC

33

I sottomoduli Git sono un po 'strani - sono sempre in modalità "testa staccata" - non si aggiornano all'ultimo commit su un ramo come ci si potrebbe aspettare.

Questo ha un senso quando ci pensi, però. Diciamo che creo un repository foo con la barra del sottomodulo . Spingo le mie modifiche e ti dico di dare un'occhiata a commit a7402be dal repository foo .

Quindi immagina che qualcuno esegua una modifica alla barra del repository prima di poter effettuare il clone.

Quando esegui il checkout del commit a7402be dal repository foo , ti aspetti di ottenere lo stesso codice che ho inviato. Ecco perché i sottomoduli non si aggiornano fino a quando non dici loro di esplicitamente e quindi fai un nuovo commit.

Personalmente penso che i sottomoduli siano la parte più confusa di Git. Ci sono molti posti che possono spiegare i sottomoduli meglio di me. Consiglio Pro Git di Scott Chacon.


Penso che sia ora di iniziare a leggere alcuni libri git, grazie per la raccomandazione.
Ivan,

Ci dispiace, ma non hai chiarito se si sarebbe ottenuto lo stesso risultato che hai spinto su a7402be, o se avessi l'ultima barra, anche se la tua versione di pippo. Grazie :)
mmm

6
Il problema è che dovrebbe esserci un'opzione per dire "mantenere questo sottomodulo sul ramo X" in modo che se VUOI aggiornarlo automaticamente, puoi farlo accadere. Renderebbe i sottomoduli molto più utili per la gestione, ad esempio, di un'installazione di WordPress in cui i plugin sono tutti repository Git senza dover salvare nuovamente il superprogetto per ogni plugin che aggiorna.
jerclarke,

@jeremyclark git clone git://github.com/git/git.gite spingi quella funzione ...? = D
Alastair,

1
La parte più confusa di Git è che anche dopo più di un decennio di sviluppo, uno strumento che mi aiuta a svolgere il mio lavoro ha ancora un'esperienza utente così negativa e per motivi completamente al di fuori di me la gente si diverte a farsi vedere da Git il tempo.
0xC0000022L

20

Per cambiare ramo per un sottomodulo (supponendo che abbiate già il sottomodulo come parte del repository):

  • cd alla radice del repository contenente i sottomoduli
  • Apri .gitmodulesper la modifica
  • Aggiungi la riga sotto path = ...e url = ...che dice branch = your-branch, per ogni sottomodulo; salva il file .gitmodules.
  • quindi senza cambiare directory fare $ git submodule update --remote

... questo dovrebbe richiamare gli ultimi commit sul ramo specificato, per ogni sottomodulo così modificato.


10

Ho questo nel mio file .gitconfig. È ancora una bozza, ma si è dimostrata utile fin d'ora. Mi aiuta a ricollegare sempre i sottomoduli al loro ramo.

[alias]

######################
#
#Submodules aliases
#
######################


#git sm-trackbranch : places all submodules on their respective branch specified in .gitmodules
#This works if submodules are configured to track a branch, i.e if .gitmodules looks like :
#[submodule "my-submodule"]
#   path = my-submodule
#   url = git@wherever.you.like/my-submodule.git
#   branch = my-branch
sm-trackbranch = "! git submodule foreach -q --recursive 'branch=\"$(git config -f $toplevel/.gitmodules submodule.$name.branch)\"; git checkout $branch'"

#sm-pullrebase :
# - pull --rebase on the master repo
# - sm-trackbranch on every submodule
# - pull --rebase on each submodule
#
# Important note :
#- have a clean master repo and subrepos before doing this !
#- this is *not* equivalent to getting the last committed 
#  master repo + its submodules: if some submodules are tracking branches 
#  that have evolved since the last commit in the master repo,
#  they will be using those more recent commits !
#
#  (Note : On the contrary, git submodule update will stick 
#to the last committed SHA1 in the master repo)
#
sm-pullrebase = "! git pull --rebase; git submodule update; git sm-trackbranch ; git submodule foreach 'git pull --rebase' "

# git sm-diff will diff the master repo *and* its submodules
sm-diff = "! git diff && git submodule foreach 'git diff' "

#git sm-push will ask to push also submodules
sm-push = push --recurse-submodules=on-demand

#git alias : list all aliases
#useful in order to learn git syntax
alias = "!git config -l | grep alias | cut -c 7-"

3

Usiamo Quack per estrarre un modulo specifico da un altro repository Git. Dobbiamo estrarre il codice senza l'intera base di codici del repository fornito - abbiamo bisogno di un modulo / file molto specifico da quel grande repository e dovremmo aggiornarlo ogni volta che eseguiamo update.

Quindi l'abbiamo raggiunto in questo modo:

Crea configurazione

name: Project Name

modules:
  local/path:
    repository: https://github.com/<username>/<repo>.git
    path: repo/path
    branch: dev
  other/local/path/filename.txt:
    repository: https://github.com/<username>/<repo>.git
    hexsha: 9e3e9642cfea36f4ae216d27df100134920143b9
    path: repo/path/filename.txt

profiles:
  init:
    tasks: ['modules']

Con la configurazione sopra, crea una directory dal repository GitHub fornito come specificato nella configurazione del primo modulo, e l'altra è estrarre e creare un file dal repository dato.

Altri sviluppatori devono solo eseguire

$ quack

E estrae il codice dalle configurazioni precedenti.


2

L'unico effetto della scelta di un ramo per un sottomodulo è che, ogni volta che si passa l' --remoteopzione nella git submodule updateriga di comando, Git eseguirà il checkout in modalità HEAD staccata (se --checkoutviene selezionato il comportamento predefinito ) l'ultimo commit di quel ramo remoto selezionato .

È necessario prestare particolare attenzione quando si utilizza questa funzione di tracciamento delle diramazioni remote per i sottomoduli Git se si lavora con cloni superficiali di sottomoduli. Il ramo scelto a tale scopo nelle impostazioni del sottomodulo NON È quello che verrà clonato durante git submodule update --remote. Se passi anche il --depthparametro e non istruisci Git su quale ramo vuoi clonare - e in realtà non puoi nella git submodule updateriga di comando !! -, si comporterà implicitamente come spiegato nella git-clone(1)documentazione per git clone --single-branchquando --branchmanca il parametro esplicito , e quindi clonerà solo il ramo primario .

Non sorprende che, dopo la fase del clone eseguita dal git submodule updatecomando, tenterà finalmente di verificare l'ultimo commit per il ramo remoto precedentemente impostato per il sottomodulo e, se questo non è quello primario, non fa parte di il tuo clone superficiale locale, e quindi fallirà

fatale: necessaria una sola revisione

Impossibile trovare l'origine corrente / Revisione NotThePrimaryBranch nel percorso del sottomodulo "mySubmodule"


come correggere l'errore - Hai bisogno di una singola revisione?
NidhinSPradeep,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.