Zaktualizuj podmoduł Git do najnowszej wersji zatwierdzenia na początku


853

Mam projekt z submodułem Git. Pochodzi z adresu URL ssh: // ... i jest na zatwierdzeniu A. Commit B został wypchnięty na ten adres URL i chcę, aby podmoduł odzyskał zatwierdzenie i zmienił go.

Teraz rozumiem, że git submodule updatepowinno to zrobić, ale tak nie jest. Nic nie robi (brak danych wyjściowych, kod zakończenia sukcesu). Oto przykład:

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule 
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...

Próbowałem zostały również git fetch mod, co wydaje się zrobić fetch (ale może nie być może, bo to nie monitowania o hasło!), Ale git logi git showzaprzeczyć istnieniu nowych zatwierdzeń. Do tej pory właśnie rmmodyfikowałem moduł i dodawałem go ponownie, ale jest to zarówno złe w zasadzie, jak i żmudne w praktyce.


5
Odpowiedź Davida Z wydaje się lepszym sposobem na zrobienie tego - teraz, gdy Git ma funkcjonalność, którą potrzebujesz wbudowaną za pomocą --remoteopcji, być może warto byłoby zaznaczyć to jako odpowiedź zaakceptowaną, a nie podejście „ręczne” w odpowiedzi Jasona?
Mark Amery

1
Zgadzam się z @MarkAmery. Chociaż Jason podał działające rozwiązanie, nie jest to zamierzony sposób, ponieważ pozostawia wskaźnik zatwierdzania podmodułu pod nieprawidłowym identyfikatorem zatwierdzenia. Nowe --remotejest zdecydowanie lepszym rozwiązaniem w tym momencie, a ponieważ pytanie to zostało powiązane z Github Gist na temat submodułów, uważam, że lepiej byłoby, aby przybywający czytelnicy zobaczyli nową odpowiedź.
MutantOctopus,

Miły akcent z hunter2hasłem: o)
lfarroco

Odpowiedzi:


1458

git submodule updateKomenda faktycznie mówi Git, że chcesz, aby submodules każdemu Sprawdź commit już określone w indeksie superproject. Jeśli chcesz zaktualizować swoje submoduły do ​​najnowszego zatwierdzenia dostępnego z ich pilota, musisz to zrobić bezpośrednio w submodułach.

Podsumowując:

# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"

Lub, jeśli jesteś zajęty:

git submodule foreach git pull origin master

335
git submodule foreach git pull
Mathias Bynens,

87
@Nicklas W takim przypadku użyj git submodule foreach git pull origin master.
Mathias Bynens,

54
W tym momencie, przy wszystkich tych poprawkach, potrzebuję kogoś, kto napisze objaśniający post na blogu i wskaże mi tam. Proszę.
Suz

25
niewielka poprawa podejścia „foreach” - możesz dodać tam opcję - rekurencyjną w przypadku, gdy masz submoduły w submodułach. tak: git submodule foreach --recursive git pull origin master.
orion elenzil

4
@Abdull -aPrzełącznik opcji git commit„Powiedz [s] komendzie, aby automatycznie dostosowywał pliki, które zostały zmodyfikowane i usunięte, ale nie dotyczy to nowych plików, o których nie powiedziałeś Gitowi”.
godfrzero

473

Git 1.8.2 oferuje nową opcję --remote, która umożliwi dokładnie takie zachowanie. Bieganie

git submodule update --remote --merge

pobierze najnowsze zmiany z wyższego poziomu w każdym podmodule, scali je i sprawdzi najnowszą wersję tego podmodułu. Jak to dokumentuje :

--zdalny

Ta opcja jest ważna tylko dla polecenia aktualizacji. Zamiast używać nagranego SHA-1 superprojektu do aktualizacji submodułu, użyj statusu gałęzi zdalnego śledzenia submodułu.

Jest to równoważne z uruchomieniem git pullw każdym podmodule, co jest generalnie dokładnie tym, czego chcesz.


4
„równoważne uruchamianiu git pullw każdym podmodule”. Aby wyjaśnić, nie ma różnicy (z perspektywy użytkownika) między odpowiedzią a git submodule foreach git pull?
Dennis,

3
@Dennis robi to w zasadzie to samo, ale nie jestem pewien, czy funkcjonalność jest dokładnie taka sama. Mogą występować drobne różnice, o których nie wiem, np. Sposób, w jaki oba polecenia reagują na niektóre ustawienia konfiguracji.
David Z

5
Chciałbym móc głosować za 10 000 razy. Dlaczego nigdzie nie jest to pokazane w dokumentacji git? Ogromny nadzór.
serraosays

4
Dla mnie faktycznie różniły się dość znacząco; foreach git pulltylko je sprawdziłem, ale nie zaktualizowałem wskaźnika głównego repozytorium, aby wskazywał na nowsze zatwierdzenie submodułu. Tylko dzięki --remoteniemu wskazywał na ostatnie zatwierdzenie.
Ela782

5
dlaczego --merge? Jaką to robi różnicę?
mFeinstein

127

W katalogu nadrzędnym projektu uruchom:

git submodule update --init

Lub jeśli masz rekurencyjne uruchamianie submodułów:

git submodule update --init --recursive

Czasami to nadal nie działa, ponieważ jakoś masz lokalne zmiany w lokalnym katalogu podmodułu podczas aktualizacji tego podmodułu.

Przez większość czasu zmiana lokalna może nie być tą, którą chcesz zatwierdzić. Może się to zdarzyć z powodu usunięcia pliku w podmodule itp. Jeśli tak, wykonaj reset w lokalnym katalogu podmodułów i katalogu nadrzędnym projektu, uruchom ponownie:

git submodule update --init --recursive

5
to jest prawdziwa odpowiedź. czy mogę jakoś przekazać go do mojego zdalnego repozytorium?
MonsterMMORPG,

Działa to w przypadku nowych submodułów! Mogę zaktualizować wszystkie pozostałe, ale folder nowych submodułów pozostanie pusty, dopóki nie uruchomię tego polecenia.
Alexis Wilke

1
Nie powoduje zmian w istniejących submodułach
Sergey G.

73

Twój główny projekt wskazuje na konkretne zatwierdzenie, w którym powinien być podmoduł. git submodule updatepróbuje sprawdzić zatwierdzenie w każdym zainicjowanym podmodule. Podmoduł jest tak naprawdę niezależnym repozytorium - po prostu utworzenie nowego zatwierdzenia w podmodule i wypychanie to nie wystarczy. Musisz także wyraźnie dodać nową wersję submodułu w głównym projekcie.

Tak więc w twoim przypadku powinieneś znaleźć właściwe zatwierdzenie w submodule - załóżmy, że to jest wskazówka master:

cd mod
git checkout master
git pull origin master

Teraz wróć do głównego projektu, przygotuj podmoduł i potwierdź, że:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

Teraz wypuść nową wersję głównego projektu:

git push origin master

Od tego momentu, jeśli ktokolwiek inny zaktualizuje swój główny projekt, to git submodule updatedla niego zaktualizuje submoduł, zakładając, że został zainicjowany.


24

Wygląda na to, że w tej dyskusji łączone są dwa różne scenariusze:

Scenariusz 1

Używając wskaźników mojego repozytorium nadrzędnego do podmodułów, chcę sprawdzić zatwierdzenie w każdym podmodule, na który wskazuje repozytorium nadrzędne, być może po pierwszej iteracji przez wszystkie podmoduły i aktualizacji / ściągnięciu ich ze zdalnego.

Jest to, jak wskazano, zrobione z

git submodule foreach git pull origin BRANCH
git submodule update

Scenariusz 2, do którego, jak sądzę, dąży

W jednym lub więcej submodułach wydarzyły się nowe rzeczy i chcę 1) pobrać te zmiany i 2) zaktualizować repozytorium nadrzędne, aby wskazywało zatwierdzenie HEAD (najnowsze) tego / tych submodułów.

Zostanie to zrobione przez

git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH

Niezbyt praktyczne, ponieważ musiałbyś zakodować n ścieżek do wszystkich n podmodułów, np. W skrypcie, aby zaktualizować wskaźniki zatwierdzenia repozytorium nadrzędnego.

Byłoby fajnie mieć zautomatyzowaną iterację przez każdy podmoduł, aktualizując wskaźnik nadrzędnego repozytorium (za pomocą git add), aby wskazywał na głowę submodułu (-ów).

W tym celu stworzyłem ten mały skrypt Bash:

git-update-submodules.sh

#!/bin/bash

APP_PATH=$1
shift

if [ -z $APP_PATH ]; then
  echo "Missing 1st argument: should be path to folder of a git repo";
  exit 1;
fi

BRANCH=$1
shift

if [ -z $BRANCH ]; then
  echo "Missing 2nd argument (branch name)";
  exit 1;
fi

echo "Working in: $APP_PATH"
cd $APP_PATH

git checkout $BRANCH && git pull --ff origin $BRANCH

git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH

Aby go uruchomić, uruchom

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

Opracowanie

Po pierwsze zakładam, że gałąź o nazwie $ BRANCH (drugi argument) istnieje we wszystkich repozytoriach. Możesz to jeszcze bardziej skomplikować.

Pierwsza para sekcji to sprawdzanie, czy istnieją argumenty. Następnie ściągam najnowsze rzeczy z repozytorium nadrzędnego (wolę używać --ff (szybkie przewijanie do przodu) za każdym razem, gdy robię ciągnięcia. Mam wyłączoną bazę, BTW).

git checkout $BRANCH && git pull --ff origin $BRANCH

W takim przypadku może być konieczne zainicjowanie niektórych modułów podrzędnych, jeśli dodano nowe moduły podrzędne lub nie zostały jeszcze zainicjowane:

git submodule sync
git submodule init
git submodule update

Następnie aktualizuję / ściągam wszystkie podmoduły:

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

Zwróć uwagę na kilka rzeczy: Po pierwsze, łączę niektóre polecenia Git za pomocą &&- co oznacza, że ​​poprzednie polecenie musi zostać wykonane bezbłędnie.

Po możliwym udanym pobraniu (jeśli nowe rzeczy zostały znalezione na pilocie), robię push, aby upewnić się, że ewentualne scalenie-zatwierdzenie nie zostanie pozostawione na kliencie. Znowu dzieje się tak tylko wtedy, gdy pociągnięcie rzeczywiście przynosi nowe rzeczy.

Wreszcie, ostatecznym || truezadaniem jest upewnienie się, że skrypt kontynuuje błędy. Aby to zadziałało, wszystko w iteracji musi być zawinięte w cudzysłowy, a polecenia Git są umieszczone w nawiasach (pierwszeństwo operatora).

Moja ulubiona część:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

Iteruj wszystkie podmoduły - za pomocą --quiet, która usuwa wyjście „Wprowadzanie MODULE_PATH”. Używając 'echo $path'(musi być w cudzysłowach), ścieżka do submodułu zostaje zapisana do wyjścia.

Ta lista względnych ścieżek podmodułu jest przechwytywana w array ( $(...)) - w końcu iteruj to i zrób git add $iaktualizację repozytorium nadrzędnego.

Na koniec zatwierdzenie z komunikatem wyjaśniającym, że repozytorium nadrzędne zostało zaktualizowane. To zatwierdzenie zostanie domyślnie zignorowane, jeśli nic nie zostało zrobione. Wciśnij to do źródła i gotowe.

Mam skrypt uruchamiający to w zadaniu Jenkins, który następnie łączy się z zaplanowanym automatycznym wdrożeniem i działa jak urok.

Mam nadzieję, że komuś to pomoże.


2
! @ # $% SO Używamy skryptów podobnych do twojego; jedna uwaga: Zamiast `` git submodule foreach - quiet 'echo $ path' '`` używamy' 'git submodule foreach --recursive --quiet pwd `` wewnątrz pętli for. pwdPolecenie drukuje właściwe „absolutną ścieżkę” dla każdego występującego submodule; --recursivezapewnia, że ​​odwiedzamy wszystkie submoduły, w tym submoduły-w ramach submodułów -..., które mogą być obecne w dużym projekcie. Obie metody powodują problemy z katalogami zawierającymi spacje, np. /c/Users/Ger/Project\ Files/...Dlatego zasadą jest, aby nigdy nie używać białych znaków nigdzie w naszych projektach.
Ger Hobbelt,

2
To miłe i masz rację, że w niektórych odpowiedziach na to pytanie jest nawet nieporozumienie, ale jak wskazała doskonała odpowiedź Davida Z, twój skrypt jest niepotrzebny, ponieważ funkcjonalność została wbudowana w Git od połowy 2013 roku, kiedy dodali --remoteopcję. git submodule update --remotezachowuje się mniej więcej tak jak skrypt.
Mark Amery

@GerHobbelt Thanks. Masz rację, mamy tylko 1 poziom submodułów, więc nigdy nie myślałem, żebym go rekurencyjny. Nie zaktualizuję skryptu, zanim nie będę mógł sprawdzić, czy działa zgodnie z oczekiwaniami, ale na pewno mój skrypt zapisałby podmoduły. Jeśli chodzi o spacje w folderach, to zdecydowanie brzmi jak coś, czego należy unikać! : S
Frederik Struck-Schøning

@MarkAmery Dziękujemy za opinię. Widzę jednak 1 problem: brak argumentu umożliwiającego określenie gałęzi dla podmodułów. Z instrukcji git: The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).Nie chcę edytować .gitmodules ani .git / config za każdym razem, gdy chcę to zrobić w innej gałęzi niż master. Ale może coś przeoczyłem? Ponadto metoda wydaje się wymuszać połączenia rekurencyjne (tym samym nie ma możliwości szybkiego przewijania do przodu).
Frederik Struck-Schøning

Ostatnia rzecz: wypróbowałem metodę @ DavidZ i wydaje się, że nie robi dokładnie takiej rzeczy, postanowiłem to zrobić (i o którą operację pytałem): dodanie zatwierdzenia HEAD podmodułów do rodzica (tj. „Aktualizacja wskaźnika” ). Wydaje się jednak, że wykonuje tę jedyną pracę bardzo dobrze (i szybciej) przy pobieraniu i scalaniu najnowszych zmian we wszystkich podmodułach. Niestety, domyślnie tylko z gałęzi master (chyba że edytujesz plik .gitmodules (patrz wyżej)).
Frederik Struck-Schøning,

19

Prosty i prosty, aby pobrać submoduły:

git submodule update --init --recursive

A teraz kontynuuj aktualizację do najnowszej gałęzi głównej (na przykład):

git submodule foreach git pull origin master

12

Uwaga, podczas gdy nowoczesną formą aktualizacji zatwierdzeń submodułu byłyby:

git submodule update --recursive --remote --merge --force

Starsza forma to:

git submodule foreach --quiet git pull --quiet origin

Z wyjątkiem ... ta druga forma nie jest tak naprawdę „cicha”.

Zobacz commit a282f5a (12 kwietnia 2019) autor: Nguyễn Thái Ngọc Duy ( pclouds) .
(Połączone przez Junio ​​C Hamano - gitster- w commit f1c9f6c , 25 kwietnia 2019)

submodule foreach: naprawa <command> --quietnie jest przestrzegana

Robin to zgłosiła

git submodule foreach --quiet git pull --quiet origin

nie jest już tak naprawdę cicho.
Powinno być cicho przed FC1b924 ( submodule: podkomenda portu submodule' foreach' z powłoki do C, 2018-05-10, Git v2.19.0-rc0), ponieważparseopt nie może przypadkowo zjeść opcji.

git pull” zachowuje się tak, jakby --quietnie został podany.

Dzieje się tak, ponieważ parseoptin submodule--helperspróbuje przeanalizować obie --quietopcje tak, jakby były opcjami foreach, a nie git-pullich.
Analizowane opcje są usuwane z wiersza poleceń. Więc kiedy ściągamy później, wykonujemy właśnie to

git pull origin

Podczas wywoływania pomocnika submodułu, dodanie „ --” przed „ git pull” zatrzyma się parseoptdla opcji parsowania, które tak naprawdę nie należą submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWN jest usuwany ze względów bezpieczeństwa. parseoptnigdy nie powinienem widzieć nieznanych opcji lub coś poszło nie tak. Istnieje również kilka aktualizacji ciągu użycia, gdy na nie patrzę.

W tym momencie dodaję także „ --” do innych podkomend, które przekazują „ $@” do submodule--helper. „ $@” w tych przypadkach są ścieżki i rzadziej będą --something-like-this.
Ale punkt nadal git-submodulejest ważny , przeanalizował i sklasyfikował, jakie są opcje, jakie są ścieżki.
submodule--helpernigdy nie powinien uważać przekazywanych ścieżek git-submoduleza opcje, nawet jeśli wyglądają jak jedna.


A Git 2.23 (III kwartał 2019 r.) Rozwiązuje inny problem: „ git submodule foreach” nie chronił opcji wiersza poleceń przekazanych do polecenia, które ma być poprawnie uruchomione w każdym podmodule, gdy --recursivebyła używana opcja „ ”.

Zobacz zatwierdzenie 30db18b (24 czerwca 2019 r.) Przez Morian Sonnet ( momoson) .
(Połączone przez Junio ​​C Hamano - gitster- w commit 968eecb , 09 lipca 2019)

submodule foreach: napraw rekursję opcji

Powołanie:

git submodule foreach --recursive <subcommand> --<option>

prowadzi do błędu stwierdzającego, że opcja --<option>jest nieznana submodule--helper.
Jest to oczywiście tylko wtedy, gdy <option>nie jest prawidłową opcją dla git submodule foreach.

Powodem tego jest to, że powyższe wywołanie jest wewnętrznie tłumaczone na wywołanie submodułu - pomocnika:

git submodule--helper foreach --recursive \
    -- <subcommand> --<option>

Wywołanie to rozpoczyna się od wykonania podkomendy z opcją w podmodule pierwszego poziomu i jest kontynuowane przez wywołanie następnej iteracji submodule foreachwywołania

git --super-prefix <submodulepath> submodule--helper \
   foreach --recursive <subcommand> --<option>

w podmodule pierwszego poziomu. Zauważ, że brakuje podwójnego myślnika przed komendą.

Ten problem zaczyna pojawiać się dopiero niedawno, ponieważ PARSE_OPT_KEEP_UNKNOWNflaga do analizy argumentów git submodule foreachzostała usunięta w zatwierdzeniu a282f5a .
Stąd narzekana jest teraz nieznana opcja, ponieważ parsowanie argumentów nie kończy się poprawnie podwójnym myślnikiem.

To zatwierdzenie rozwiązuje problem poprzez dodanie podwójnego myślnika przed podkomendą podczas rekurencji.


7
git pull --recurse-submodules

Spowoduje to ściągnięcie wszystkich najnowszych zatwierdzeń.


4

W moim przypadku chciałem git zaktualizować do najnowszej wersji i jednocześnie uzupełnić brakujące pliki.

Następujące pliki przywróciły brakujące pliki (dzięki --forceczemu nie wydaje się tutaj wspomniane), ale nie pobrały żadnych nowych zatwierdzeń:

git submodule update --init --recursive --force

To spowodowało:

git submodule update --recursive --remote --merge --force


3

@Jason ma rację, ale nie do końca.

aktualizacja

Zaktualizuj zarejestrowane podmoduły, tj. Sklonuj brakujące podmoduły i sprawdź zatwierdzenie określone w indeksie zawierającego repozytorium. Spowoduje to odłączenie HEAD podmodułów, chyba że podano --rebase lub --merge lub podmoduł klucza. $ Name.update jest ustawiony na rebase lub scalanie.

Tak git submodule updatesamo robi kasy, ale dotyczy zatwierdzenia w indeksie zawierającego repozytorium. W ogóle jeszcze nie wie o nowym zatwierdzeniu na wcześniejszym etapie. Więc przejdź do swojego podmodułu, pobierz żądane zatwierdzenie i zatwierdź zaktualizowany stan podmodułu w głównym repozytorium, a następnie wykonaj git submodule update.


1
Wygląda na to, że jeśli przestawię podmoduł do innego zatwierdzenia, a następnie uruchomię git submodule update, aktualizacja przeniesie podmoduł do zatwierdzenia określonego w bieżącej HEAD superprojektu. (cokolwiek ostatnie zatwierdzenie w superprojekcie mówi, że podprojekt powinien być - takie zachowanie, po wyjaśnieniu w poście Jasona, wydaje mi się logiczne) Wydaje się również, że jest pobierane, ale tylko w przypadku, gdy podprojekt ma niewłaściwe zatwierdzenie , co dodawało mi zamieszania.
Thanatos,

2

Oto niesamowity one-liner do aktualizacji wszystkiego do najnowszej wersji master:

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Dzięki Mark Jaquith


2

Jeśli nie znasz gałęzi hosta, zrób to:

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

Otrzyma gałąź głównego repozytorium Git, a następnie dla każdego podmodułu wykona tę samą gałąź.


0

Jeśli chcesz masterpobrać gałąź dla każdego podmodułu - możesz w tym celu użyć następującego polecenia:

git submodule foreach git checkout master
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.