Perché il mio sottomodulo Git HEAD è staccato dal master?


163

Sto usando i sottomoduli Git. Dopo aver estratto le modifiche dal server, molte volte la mia testa del sottomodulo viene staccata dal ramo principale.

Perché succede?

Devo sempre fare:

git branch
git checkout master

Come posso assicurarmi che il mio sottomodulo punta sempre al ramo principale?


1
Hai letto questa risposta? stackoverflow.com/questions/1777854/…
Johnny Z,

@bitoiu Ho guardato subtree e Google Repo. Non ho ancora una soluzione perfetta :(
om471987

1
la mia esperienza con gitsubmodules, in un ambiente CI è terribile, forse altre persone hanno esperienze migliori.
Bitoiu,

@JohnnyZ Grazie. Ho capito che il sottomodulo indica un commit e non un capo dell'albero. Ma perché staccato dal ramo. Se hai un ramo non dovrebbe essere collegato ad esso per impostazione predefinita
om471987

3
Non essere troppo veloce per eliminare i sottomoduli solo perché hai sentito che sono cattivi. Sono una soluzione scadente se si desidera l'integrazione continua, ma sono una soluzione quasi perfetta se si desidera incorporare il codice da un progetto esterno e gestire esplicitamente tutti i pull. Questa è spesso la migliore pratica se ti stai integrando con un modulo non biforcato che non è controllato dalla tua organizzazione. Il problema è che sono una soluzione allettante in tutti i tipi di altre situazioni in cui non funzionano affatto molto bene. Il miglior consiglio è quello di leggere su come funzionano e valutare il tuo scenario.
Sarah G,

Risposte:


176

MODIFICARE:

Vedi la risposta di @Simba per una soluzione valida

submodule.<name>.updateè ciò che si desidera modificare, vedere i documenti - impostazione predefinitacheckout
submodule.<name>.branch specificare il ramo remoto da tracciare - impostazione predefinitamaster


VECCHIA RISPOSTA:

Personalmente odio le risposte che si rivolgono a collegamenti esterni che potrebbero smettere di funzionare nel tempo e controllare la mia risposta qui (a meno che la domanda non sia duplicata) - indirizzare alla domanda che copre l'argomento tra le righe dell'altro argomento, ma nel complesso è uguale: "Sono non rispondendo, leggi la documentazione. "

Quindi torniamo alla domanda: perché succede?

Situazione che hai descritto

Dopo aver estratto le modifiche dal server, molte volte la mia testa del sottomodulo viene staccata dal ramo principale.

Questo è un caso comune quando uno non usa i sottomoduli troppo spesso o ha appena iniziato con sottomoduli . Credo di aver ragione nell'affermare, che siamo stati tutti lì ad un certo punto in cui la TESTA del nostro sottomodulo si stacca.

  • Causa: il sottomodulo non sta monitorando il ramo corretto (master predefinito).
    Soluzione: assicurarsi che il sottomodulo stia monitorando il ramo corretto
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • Causa: il repository principale non è configurato per tenere traccia del ramo dei sottomoduli.
    Soluzione: fai in modo che il tuo sottomodulo segua il suo ramo remoto aggiungendo nuovi sottomoduli con i seguenti due comandi.
    • Per prima cosa dici a git di localizzare il tuo telecomando <branch>.
    • dici a git di eseguire rebase o merge invece di checkout
    • dici a git di aggiornare il tuo sottomodulo da remoto.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Se non hai aggiunto il tuo sottomodulo esistente in questo modo, puoi facilmente risolvere il problema:
    • Prima di tutto, assicurati che il tuo sottomodulo abbia il ramo estratto che desideri monitorare.
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

Nei casi comuni, hai già risolto il tuo DETACHED HEAD dal momento che era correlato a uno dei problemi di configurazione sopra.

fissaggio TESTA STACCATA quando .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

Ma se sei riuscito a apportare alcune modifiche localmente già per il sottomodulo e hai eseguito il commit, le hai spinte sul telecomando poi quando hai eseguito 'git checkout', Git ti avvisa:

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

L'opzione raccomandata per creare un ramo temporaneo può essere buona, quindi puoi unire questi rami ecc. Tuttavia, personalmente utilizzerei solo git cherry-pick <hash>in questo caso.

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

Anche se ci sono altri casi in cui puoi portare i tuoi sottomoduli nello stato DETACHED HEAD, spero che tu capisca ora un po 'di più come eseguire il debug del tuo caso particolare.


2
HEAD distaccato è il comportamento predefinito di git submodule update --remote. Dai un'occhiata alla risposta di Simba, penso che dovrebbe essere la risposta giusta.
Magomar,

78

L'aggiunta di un branchopzione in .gitmoduleè non relative al comportamento distaccato di sottomoduli a tutti. La vecchia risposta di @mkungla è errata o obsoleta.

Da git submodule --help, HEAD rimosso è il comportamento predefinito di git submodule update --remote.

Innanzitutto, non è necessario specificare un ramo da tracciare . origin/masterè il ramo predefinito da tracciare.

--a distanza

Invece di utilizzare lo SHA-1 registrato del superprogetto per aggiornare il sottomodulo, utilizzare lo stato del ramo di tracciamento remoto del sottomodulo. Il telecomando utilizzato è il ramo ( branch.<name>.remote) di defaultorigin , predefinito . Il ramo remoto ha utilizzato le impostazioni predefinite sumaster .

Perché

Quindi perché HEAD viene rimosso dopo update? Ciò è causato dal comportamento di aggiornamento modulo predefinito:checkout .

--check-out

Verifica il commit registrato nel superprogetto su una HEAD staccata nel sottomodulo. Questo è il comportamento predefinito , l'uso principale di questa opzione è quello di sovrascrivere submodule.$name.updatequando impostato su un valore diverso da checkout.

Per spiegare questo strano comportamento di aggiornamento, dobbiamo capire come funzionano i sottomoduli?

Citazione da Iniziare con i sottomoduli nel libro Pro Git

Sebbene sbmodule DbConnectorsia una sottodirectory nella directory di lavoro, Git la vede come un sottomodulo e non ne traccia il contenuto quando non ci si trova in quella directory. Invece, Git lo vede come un commit particolare da quel repository .

Il repository principale tiene traccia del sottomodulo con il suo stato in un punto specifico , l' id di commit . Quindi, quando aggiorni i moduli, stai aggiornando l'ID commit con uno nuovo.

Come

Se si desidera che il sottomodulo venga unito automaticamente al ramo remoto, utilizzare --mergeo --rebase.

--merge

Questa opzione è valida solo per il comando di aggiornamento . Unire il commit registrato nel superprogetto nel ramo corrente del sottomodulo. Se viene fornita questa opzione, HEAD del sottomodulo non verrà rimosso .

--rebase

Rebase il ramo corrente sul commit registrato nel superprogetto. Se viene fornita questa opzione, HEAD del sottomodulo non verrà rimosso .

Tutto quello che devi fare è

git submodule update --remote --merge
# or
git submodule update --remote --rebase

Alias ​​consigliato:

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

C'è anche un'opzione per creare --mergeo --rebasecome comportamento predefinito di git submodule update, impostando submodule.$name.updatesu mergeo rebase.

Ecco un esempio su come configurare il comportamento di aggiornamento predefinito dell'aggiornamento del sottomodulo in .gitmodule.

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

Oppure configuralo nella riga di comando,

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

Riferimenti


6
Io uso git submodule update --remote --merge, e abbassa il sottomodulo in uno stato distaccato. Anche provato --rebasecon lo stesso risultato.
Joe Strout,

8
@JoeStrout Se il tuo sottomodulo è già stato rimosso, correggi lo stato prima di eseguire un aggiornamento con i comandi precedenti. cdnel modulo, verifica il modulo ad un ramo specifico con, git checkout master.
Simba

2
Oppure - se questo è troppo fastidioso per più sottomoduli (ricorsivi) - semplicemente git submodule foreach --recursive git checkout master.
stefanct,

1
Capisco solo parzialmente le descrizioni "come funziona git". TBH Non mi interessa davvero capire come funziona git, voglio solo usarlo. Ora capisco che posso correggere i sottomoduli staccati con git submodule foreach --recursive git checkout master. Ma come posso impedire a Git di staccarli sempre? L'impostazione delle opzioni di configurazione per ciascun sottomodulo non è un'opzione!
Nicolas

Per me, l'esecuzione git submodule update --remote --mergenon ha lasciato il sottomodulo in uno stato HEAD distaccato, ma l'esecuzione git submodule updatedopo aver modificato il mio .gitmodulefile come indicato DID ha lasciato il sottomodulo in uno stato HEAD distaccato.
bweber13

41

mi sono stancato di staccarmi sempre, quindi uso solo uno script di shell per crearlo per tutti i miei moduli. presumo che tutti i sottomoduli siano sul master: ecco lo script:

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

eseguilo dal tuo modulo genitore


2
git sottomodulo foreach git pull origin master - questo è quello che stavo cercando .. kudos
csomakk,

semplice e conciso! Grazie!
Zhekaus,

12

Dai un'occhiata alla mia risposta qui: Git submodules: specifica un ramo / tag

Se lo desideri, puoi aggiungere manualmente la riga "branch = master" nel tuo file .gitmodules. Leggi il link per vedere cosa intendo.

EDIT: per tenere traccia di un progetto di sottomodulo esistente in una filiale, seguire invece le istruzioni di VonC qui:

Git submodules: specifica un ramo / tag


14
Si suppone che le risposte siano in linea; Il collegamento IIRC alle risposte è un passo falso Stack Overflow.
Tony Topper,

1
@TonyTopper Anche quando ti colleghi ad un'altra risposta SO? IIRC osserva soltanto i collegamenti esterni poiché questi potrebbero scomparire e quindi il collegamento è morto e la risposta è, beh, inutile. Eppure non c'è un tale pericolo con le risposte SO, non andranno mai via, a meno che SO non scompaia (e possa essere ripristinato qualunque cosa accada). Inoltre ha risposto alla domanda, come branch = master" line into your .gitmodulein effetti è la risposta completa, risolto il problema per me.
Mecki,

9

L'altro modo per creare il sottomodulo per estrarre il ramo è di andare al .gitmodulesfile nella cartella principale e aggiungere il campo branchnella configurazione del modulo come segue:

branch = <branch-name-you-want-module-to-checkout>


15
Per me questo non funziona. Ho impostato correttamente branch = my_wanted_branch. Ma eseguirlo git submodule update --remotesi verifica ancora come testa distaccata.
Andrius,

Fai questo, quindi cd sudmodule & git co thebranche & cd .., quindi git submodule update --remote e funziona!
pdem,

Non è così che '.gitmodules' è in uso attivo (viene letto) solo mentre il superprogetto viene clonato in modo ricorsivo sottomodulo o inizializzato sottomodulo? In altre parole, il tuo repository aggiorna il file in cui non sempre approfitta degli aggiornamenti di configurazione del sottomodulo messi in ".gitmodules". Nella mia comprensione '.gitmodules' è un modello per la configurazione creato mentre il repository viene clonato o giù di lì.
Na13-c,

3

Come altri hanno già detto, la ragione per cui ciò accade è che il repository principale contiene solo un riferimento a (il SHA1 di) un commit specifico nel sottomodulo - non sa nulla sui rami. Ecco come dovrebbe funzionare: il ramo che si trovava a quel commit potrebbe essersi spostato in avanti (o all'indietro) e se il repository principale aveva fatto riferimento al ramo, potrebbe facilmente rompersi quando ciò accade.

Tuttavia, specialmente se si sta sviluppando attivamente sia nel repository principale sia nel sottomodulo, lo detached HEADstato può essere confuso e potenzialmente pericoloso. Se effettui commit nel sottomodulo mentre è nello detached HEADstato, questi diventano penzolanti e puoi facilmente perdere il lavoro. (Di solito è possibile salvare i commit sospesi usando git reflog, ma è molto meglio evitarli in primo luogo.)

Se sei come me, la maggior parte delle volte se nel sottomodulo è presente un ramo che indica che il commit è stato estratto, preferiresti controllare quel ramo piuttosto che essere nello stato HEAD distaccato nello stesso commit. Puoi farlo aggiungendo il seguente alias al tuo gitconfigfile:

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

Ora, dopo aver fatto ciò, git submodule updatedevi solo chiamare git submodule-checkout-branche qualsiasi sottomodulo che viene estratto in un commit che ha un ramo che punta ad esso controllerà quel ramo. Se non hai spesso più filiali locali che puntano tutte allo stesso commit, questo di solito farà ciò che desideri; in caso contrario, almeno assicurerà che qualsiasi commit effettuato faccia andare su un ramo effettivo invece di essere lasciato penzolare.

Inoltre, se hai impostato git per aggiornare automaticamente i sottomoduli al checkout (usando git config --global submodule.recurse true, vedi questa risposta ), puoi fare un hook post-checkout che chiama automaticamente questo alias:

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

Quindi non è necessario chiamare nessuno dei due git submodule updateo git submodule-checkout-branch, semplicemente facendo git checkoutaggiornerà tutti i sottomoduli ai rispettivi commit e verificherà i rami corrispondenti (se presenti).


0

La soluzione più semplice è:

git clone --recursive git@github.com:name/repo.git

Quindi cd nella directory repo e:

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

Letture aggiuntive: best practice per i sottomoduli Git .

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.