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.

Tanatos
źródło
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
Jason
źródło
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.

David Z
źródło
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
pinux
źródło
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.

Mark Longair
źródło
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.

Frederik Struck-Schøning
źródło
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
Daniel Andrei Mincă
źródło
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.

VonC
źródło
7
git pull --recurse-submodules

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


źródło
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

noseratio
źródło
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.

manojlds
źródło
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

dustinrwh
źródło
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łąź.

NickUnuchek
źródło
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
Mohsin Mahmood
źródło