Dlaczego mój Git Submodule HEAD jest odłączony od głównego?

162

Używam podmodułów Git. Po pobraniu zmian z serwera wiele razy moja głowa podmodułu jest odłączana od gałęzi głównej.

Dlaczego tak się dzieje?

Muszę zawsze robić:

git branch
git checkout master

Jak mogę się upewnić, że mój moduł podrzędny zawsze wskazuje gałąź główną?

om471987
źródło
1
Czy przeczytałeś tę odpowiedź? stackoverflow.com/questions/1777854/…
Johnny Z
@bitoiu Spojrzałem na poddrzewo i Google Repo. Nie mam jeszcze idealnego rozwiązania :(
om471987
1
moje doświadczenie z gitsubmodules, w środowisku CI jest okropne, może inni mają lepsze doświadczenia.
bitoiu
@JohnnyZ Thanks. Zrozumiałem, że podmoduł wskazuje na zatwierdzenie, a nie głowę drzewa. Ale dlaczego oderwany od gałęzi. Jeśli masz jedną gałąź, nie powinieneś jej domyślnie do niej
dołączać
3
Nie odrzucaj modułów podrzędnych zbyt szybko tylko dlatego, że słyszałeś, że są złe. Są kiepskim rozwiązaniem, jeśli chcesz ciągłej integracji, ale są prawie idealnym rozwiązaniem, jeśli chcesz osadzić kod z zewnętrznego projektu i jawnie zarządzasz wszystkimi ściągnięciami. Jest to często najlepsza praktyka w przypadku integracji z nierozdzielonym modułem, który nie jest kontrolowany przez Twoją organizację. Problem w tym, że są kuszącym rozwiązaniem we wszystkich innych sytuacjach, w których nie działają zbyt dobrze. Najlepszą radą jest przeczytanie, jak działają i ocena scenariusza.
Sarah G

Odpowiedzi:

176

EDYTOWAĆ:

Prawidłowe rozwiązanie znajdziesz w @Simba Answer

submodule.<name>.updateto, co chcesz zmienić, zobacz dokumentację - domyślniecheckout
submodule.<name>.branch określ zdalną gałąź do śledzenia - domyślniemaster


STARA ODPOWIEDŹ:

Osobiście nienawidzę tutaj odpowiedzi kierujących do zewnętrznych linków, które z czasem mogą przestać działać i sprawdź moją odpowiedź tutaj (chyba że pytanie jest powtórzone) - kierując do pytania, które obejmuje temat między wierszami innego tematu, ale ogólnie jest równe: „Jestem nie odpowiadając, przeczytaj dokumentację. ”

Wróćmy więc do pytania: dlaczego tak się dzieje?

Sytuacja, którą opisałeś

Po pobraniu zmian z serwera wiele razy moja głowa podmodułu jest odłączana od gałęzi głównej.

Jest to częsty przypadek, gdy nie używa się podmodułów zbyt często lub dopiero zaczynamy z modułami podrzędnymi . Uważam, że mam rację stwierdzając, że wszyscy byliśmy w pewnym momencie, w którym nastąpiło odłączenie HEAD naszego submodułu .

  • Przyczyna: Twój moduł podrzędny nie śledzi prawidłowej gałęzi (domyślny master).
    Rozwiązanie: upewnij się, że podmoduł śledzi prawidłową gałąź
$ 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>
  • Przyczyna: Twoje nadrzędne repozytorium nie jest skonfigurowane do śledzenia gałęzi modułów podrzędnych.
    Rozwiązanie: spraw, aby moduł podrzędny śledził jego gałąź zdalną, dodając nowe moduły podrzędne za pomocą następujących dwóch poleceń.
    • Najpierw każesz gitowi śledzić twojego pilota <branch>.
    • mówisz gitowi, aby wykonał rebase lub merge zamiast checkout
    • mówisz gitowi, aby zaktualizował twój moduł podrzędny z pilota.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Jeśli nie dodałeś swojego istniejącego modułu podrzędnego w ten sposób, możesz łatwo to naprawić:
    • Najpierw chcesz się upewnić, że Twój podmoduł ma zaznaczoną gałąź, którą chcesz śledzić.
    $ 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>

W typowych przypadkach już naprawiłeś ODŁĄCZONEJ GŁOWICY, ponieważ było to związane z jednym z powyższych problemów z konfiguracją.

mocowanie ODŁĄCZONEJ GŁOWY, gdy .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.

Ale jeśli udało ci się wprowadzić pewne zmiany lokalnie już dla modułu podrzędnego i zatwierdziłeś, przekazałeś je do zdalnego, a następnie po wykonaniu polecenia „git checkout”, Git powiadamia:

$ 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:

Zalecana opcja tworzenia tymczasowej gałęzi może być dobra, a następnie możesz po prostu scalić te gałęzie itp. Jednak osobiście użyłbym tylko git cherry-pick <hash>w tym przypadku.

$ 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>

Chociaż istnieje więcej przypadków, w których można wprowadzić moduły podrzędne w stan ODŁĄCZONA GŁOWA, mam nadzieję, że teraz rozumiesz nieco więcej, jak debugować konkretny przypadek.

mkungla
źródło
2
HEAD odłączony jest domyślnym zachowaniem git submodule update --remote. Proszę spojrzeć na odpowiedź Simby, myślę, że to powinna być właściwa odpowiedź.
magomar
78

Dodanie branchopcji w NIE.gitmodule jest powiązane z jednorodzinnym zachowania submodules w ogóle. Stara odpowiedź od @mkungla jest nieprawidłowa lub nieaktualna.

Od git submodule --help, odłączony HEAD jest domyślnym zachowaniem programugit submodule update --remote .

Po pierwsze, nie ma potrzeby określania gałęzi do śledzenia . origin/masterjest domyślną gałęzią do śledzenia.

--zdalny

Zamiast używać zarejestrowanego SHA-1 superprojektu do aktualizacji podmodułu, użyj statusu gałęzi zdalnego śledzenia podmodułu. Użyty pilot to remote ( branch.<name>.remote) oddziału , domyślnieorigin . Używana gałąź zdalna domyślniemaster .

Czemu

Dlaczego więc HEAD jest odłączany po update? Jest to spowodowane przez domyślne zachowanie aktualizacji modułu:checkout .

--sprawdzić

Sprawdź zatwierdzenie zapisane w superprojekcie na odłączonej HEAD w module podrzędnym. Jest to zachowanie domyślne , głównym zastosowaniem tej opcji jest przesłonięcie, submodule.$name.updategdy jest ustawiona na wartość inną niż checkout.

Aby wyjaśnić to dziwne zachowanie podczas aktualizacji, musimy zrozumieć, jak działają moduły podrzędne?

Cytat z książki „Rozpoczęcie od podmodułów” w książce Pro Git

Chociaż sbmodule DbConnectorjest podkatalogiem w twoim katalogu roboczym, Git widzi go jako podmoduł i nie śledzi jego zawartości, gdy nie jesteś w tym katalogu. Zamiast tego Git postrzega to jako określone zatwierdzenie z tego repozytorium .

Główne repozytorium śledzi podmoduł z jego stanem w określonym punkcie , identyfikatorem zatwierdzenia . Więc kiedy aktualizujesz moduły, aktualizujesz identyfikator zatwierdzenia na nowy.

W jaki sposób

Jeśli chcesz, aby moduł podrzędny został automatycznie połączony ze zdalną gałęzią, użyj --mergelub --rebase.

--łączyć

Ta opcja jest ważna tylko dla polecenia aktualizacji . Połącz zatwierdzenie zapisane w superprojekcie z bieżącą gałęzią podmodułu. Jeśli podano tę opcję, HEAD submodułu nie zostanie odłączony .

--rebase

Przebuduj bieżącą gałąź na zatwierdzenie zapisane w superprojekcie. Jeśli podano tę opcję, HEAD submodułu nie zostanie odłączony .

Wszystko, co musisz zrobić, to

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

Zalecany alias:

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

# do submodule update with
git supdate

Istnieje również możliwość, aby zrobić --mergelub --rebasejako domyślnego zachowania git submodule update, przez ustawienie submodule.$name.updatesię mergelubrebase .

Oto przykład dotyczący konfigurowania domyślnego zachowania aktualizacji modułu podrzędnego w programie .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

Lub skonfiguruj go w linii poleceń,

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

Bibliografia

Simba
źródło
6
Używam git submodule update --remote --mergei rozłącza moduł podrzędny w stanie odłączonym. Próbowałem również --rebasez tym samym wynikiem.
Joe Strout
8
@JoeStrout Jeśli moduł podrzędny został już odłączony, napraw stan odłączenia przed wykonaniem aktualizacji za pomocą powyższych poleceń. cddo modułem, checkout submodule do oddziału szczególnego z, git checkout master.
Simba
2
Lub - jeśli jest to zbyt kłopotliwe dla wielu (rekurencyjnych) modułów podrzędnych - po prostu zrób to git submodule foreach --recursive git checkout master.
stefanct
1
Tylko częściowo rozumiem opisy „jak działa git”. TBH Naprawdę nie interesuje mnie zrozumienie, jak działa git, po prostu chcę go użyć. Teraz rozumiem, że mogę naprawić odłączone moduły podrzędne za pomocą git submodule foreach --recursive git checkout master. Ale jak mogę zapobiec ciągłemu odłączaniu ich przez git? Ustawianie opcji konfiguracyjnych dla każdego modułu podrzędnego nie wchodzi w grę!
Nicolas
Dla mnie uruchomienie git submodule update --remote --mergenie pozostawiło modułu podrzędnego w stanie odłączonego HEAD, ale działanie git submodule updatepo edycji mojego .gitmodulepliku, jak wskazałeś, NIE pozostawiło podmodułu w stanie odłączonym HEAD.
bweber13
41

Mam dość tego, że zawsze się odłączam, więc po prostu używam skryptu powłoki, aby zbudować go dla wszystkich moich modułów. zakładam, że wszystkie podmoduły są na master: oto skrypt:

#!/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 

wykonaj go z modułu nadrzędnego

j2emanue
źródło
2
git submodule foreach git pull origin master - właśnie tego szukałem .. kudos
csomakk
proste i zwięzłe! Dzięki!
zhekaus
12

Sprawdź moją odpowiedź tutaj: Podmoduły Git: Określ gałąź / tag

Jeśli chcesz, możesz ręcznie dodać wiersz "branch = master" do pliku .gitmodules. Przeczytaj link, aby zobaczyć, co mam na myśli.

EDYCJA: Aby śledzić istniejący projekt podmodułu w oddziale, postępuj zgodnie z instrukcjami VonC tutaj:

Podmoduły Git: określ gałąź / tag

Johnny Z
źródło
14
Odpowiedzi mają być wbudowane; Łączenie IIRC z odpowiedziami to faux pas.
Tony Topper
1
@TonyTopper Nawet jeśli łączysz się z inną odpowiedzią SO? Tylko linki zewnętrzne IIRC są marszczone, ponieważ mogą zniknąć, a następnie link jest martwy, a odpowiedź jest, cóż, bezużyteczna. Jednak w przypadku odpowiedzi SO nie ma takiego niebezpieczeństwa, nigdy nie znikną, chyba że SO zniknie (i można je przywrócić bez względu na to, co się stanie). On również odpowiedział na pytanie, tak jak branch = master" line into your .gitmodulew rzeczywistości pełna odpowiedź, rozwiązał ten problem za mnie.
Mecki
9

Innym sposobem, aby moduł podrzędny wyewidencjonował gałąź, jest przejście do .gitmodulespliku w folderze głównym i dodanie pola branchw konfiguracji modułu w następujący sposób:

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

frontendgirl
źródło
15
U mnie to nie działa. Ustawiłem poprawnie branch = my_wanted_branch. Ale bieganie git submodule update --remotenadal sprawdza się jako odłączona głowa.
Andrius
Zrób to, a następnie cd sudmodule & git co thebranche & cd .., a następnie aktualizacja podmodułu git --remote i działa!
pdem
Czy nie jest tak, że „.gitmodules” jest w aktywnym użyciu (jest odczytywane) tylko wtedy, gdy superprojekt jest klonowany w podmodułach - rekurencyjnie lub inicjalizowany podmoduł? Innymi słowy, we własnym repozytorium aktualizujesz plik, w którym nie zawsze korzystasz z aktualizacji konfiguracji podmodułów umieszczonych w '.gitmodules'. W moim rozumieniu „.gitmodules” jest szablonem konfiguracji utworzonym podczas klonowania repozytorium.
Na13-c
3

Jak powiedzieli inni ludzie, powodem tego jest to, że nadrzędne repozytorium zawiera tylko odniesienie do (SHA1) konkretnego zatwierdzenia w podmodułu - nie wie nic o gałęziach. Tak to powinno działać: gałąź, która była w tym zatwierdzeniu, mogła przesunąć się do przodu (lub do tyłu), a jeśli nadrzędne repozytorium odwoływało się do gałęzi, może łatwo się zepsuć, gdy tak się stanie.

Jednakże, szczególnie jeśli aktywnie rozwijasz się zarówno w nadrzędnym repozytorium, jak i w module podrzędnym, detached HEADstan może być zagmatwany i potencjalnie niebezpieczny. Jeśli wykonasz zatwierdzenia w module podrzędnym, gdy jest on w detached HEADstanie, zaczną one zawieszać się i możesz łatwo stracić pracę. (Wiszące zatwierdzenia można zwykle uratować za pomocą git reflog, ale o wiele lepiej jest ich unikać.)

Jeśli jesteś podobny do mnie, to przez większość czasu, jeśli w module podrzędnym znajduje się gałąź wskazująca na sprawdzanie zatwierdzenia, wolałbyś raczej sprawdzić tę gałąź niż być w stanie odłączonego HEAD w tym samym zatwierdzeniu. Możesz to zrobić, dodając następujący alias do swojego gitconfigpliku:

[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"

Teraz, po wykonaniu tej czynności git submodule update, wystarczy wywołać git submodule-checkout-branch, a każdy podmoduł, który jest pobrany w zatwierdzeniu, który ma gałąź wskazującą na niego, sprawdzi tę gałąź. Jeśli nie masz często wielu gałęzi lokalnych, z których wszystkie wskazują na to samo zatwierdzenie, to zwykle zrobi to, co chcesz; jeśli nie, to przynajmniej zapewni, że wszystkie wykonane przez Ciebie zatwierdzenia trafią do rzeczywistej gałęzi, zamiast pozostawać w zawieszeniu.

Ponadto, jeśli skonfigurowałeś git, aby automatycznie aktualizował podmoduły przy kasie (używając git config --global submodule.recurse true, zobacz tę odpowiedź ), możesz zrobić haczyk po kasie, który automatycznie wywołuje ten alias:

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

Wtedy nie musisz wywoływać żadnego z nich git submodule updatelub git submodule-checkout-branch, po prostu git checkoutspowoduje to zaktualizowanie wszystkich podmodułów do odpowiednich zatwierdzeń i sprawdzenie odpowiednich gałęzi (jeśli istnieją).

deltacrux
źródło
0

Najprostszym rozwiązaniem jest:

git clone --recursive [email protected]:name/repo.git

Następnie cd w katalogu repozytorium i:

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

Dodatkowa lektura: najlepsze praktyki git submodules .

noccoa0
źródło