Aktualisieren Sie das Git-Submodul auf das neueste Commit für den Ursprung


853

Ich habe ein Projekt mit einem Git-Submodul. Es stammt von einer ssh: // ... -URL und befindet sich auf Commit A. Commit B wurde auf diese URL verschoben, und ich möchte, dass das Submodul das Commit abruft und zu diesem wechselt.

Mein Verständnis ist, dass dies git submodule updategetan werden sollte, aber es tut es nicht. Es macht nichts (keine Ausgabe, Erfolgs-Exit-Code). Hier ist ein Beispiel:

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

Ich habe auch versucht git fetch mod, das einen Abruf zu tun erscheint (aber kann unmöglich, weil es nicht für ein Kennwort eingeben müssen !), Aber git logund git showleugnen die Existenz von neuen Commits. Bisher habe ich rmdas Modul nur bearbeitet und erneut hinzugefügt, aber dies ist im Prinzip sowohl falsch als auch in der Praxis mühsam.


5
Die Antwort von David Z scheint der bessere Weg zu sein - jetzt, da Git über die --remoteOption die Funktionalität verfügt, die Sie benötigen , wäre es vielleicht nützlich, dies als akzeptierte Antwort und nicht als "von Hand" -Ansatz in Jasons Antwort zu markieren.
Mark Amery

1
Ich stimme @MarkAmery sehr zu. Obwohl Jason eine funktionierende Lösung angegeben hat, ist dies nicht die beabsichtigte Methode, da der Festschreibungszeiger des Submoduls an der falschen Festschreibungskennung verbleibt. Das Neue --remoteist zu diesem Zeitpunkt definitiv eine bessere Lösung, und da diese Frage von einem Github Gist zu Submodulen verknüpft wurde, halte ich es für besser, wenn eingehende Leser die neue Antwort sehen.
MutantOctopus

Nette Berührung mit dem hunter2Passwort: o)
lfarroco

Antworten:


1458

Der git submodule updateBefehl teilt Git tatsächlich mit, dass Ihre Submodule jedes Mal das Commit auschecken sollen, das bereits im Index des Superprojekts angegeben ist. Wenn Sie Ihre Submodule auf das neueste Commit aktualisieren möchten , das von ihrer Fernbedienung verfügbar ist, müssen Sie dies direkt in den Submodulen tun.

Also zusammenfassend:

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

Oder wenn Sie beschäftigt sind:

git submodule foreach git pull origin master

335
git submodule foreach git pull
Mathias Bynens

87
@ Nicklas In diesem Fall verwenden git submodule foreach git pull origin master.
Mathias Bynens

54
An diesem Punkt, mit all diesen Korrekturen an den Korrekturen, brauche ich jemanden, der einen erklärenden Blog-Beitrag schreibt und mich dort zeigt. Bitte.
Suz

25
geringfügige Verbesserung des 'foreach'-Ansatzes - Sie möchten möglicherweise hinzufügen - rekursiv, falls Sie Submodule innerhalb von Submodulen haben. also : git submodule foreach --recursive git pull origin master.
Orion Elenzil

4
@Abdull Der -aSchalter für git commit" Weist den Befehl an, Dateien, die geändert und gelöscht wurden, automatisch bereitzustellen , aber neue Dateien, von denen Sie Git nichts erzählt haben, sind nicht betroffen."
Godfrzero

473

Git 1.8.2 bietet eine neue Option, --remotedie genau dieses Verhalten ermöglicht. Laufen

git submodule update --remote --merge

ruft die neuesten Änderungen aus dem Upstream in jedem Submodul ab, führt sie zusammen und überprüft die neueste Version des Submoduls. Wie die Dokumentation es ausdrückt:

--Fernbedienung

Diese Option gilt nur für den Aktualisierungsbefehl. Verwenden Sie den Status des Remote-Tracking-Zweigs des Submoduls, anstatt den aufgezeichneten SHA-1 des Superprojekts zum Aktualisieren des Submoduls zu verwenden.

Dies entspricht der Ausführung git pullin jedem Submodul, was im Allgemeinen genau das ist, was Sie wollen.


4
"Entspricht dem Ausführen git pullin jedem Submodul" Zur Verdeutlichung gibt es keinen Unterschied (aus Sicht des Benutzers) zwischen Ihrer Antwort und git submodule foreach git pull?
Dennis

3
@ Tennis macht es im Wesentlichen das gleiche, aber ich bin nicht sicher, ob die Funktionalität genau die gleiche ist. Es kann einige geringfügige Unterschiede geben, die ich nicht kenne, z. B. in der Art und Weise, wie die beiden Befehle auf bestimmte Konfigurationseinstellungen reagieren.
David Z

5
Ich wünschte, ich könnte dieses 10.000-fache verbessern. Warum wird dies in der Dokumentation von git nirgendwo gezeigt? Riesiges Versehen.
Serraosays

4
Für mich unterschieden sie sich tatsächlich ziemlich stark; foreach git pullhat sie nur ausgecheckt, aber den Zeiger des Haupt-Repos nicht aktualisiert, um auf das neuere Commit des Submoduls zu verweisen. Nur --remotedamit wurde auf das letzte Commit hingewiesen.
Ela782

5
Warum die Option --merge? Welchen Unterschied macht es?
mFeinstein

127

Führen Sie in Ihrem übergeordneten Projektverzeichnis Folgendes aus:

git submodule update --init

Oder wenn Sie rekursive Submodule ausführen lassen:

git submodule update --init --recursive

Manchmal funktioniert dies immer noch nicht, weil Sie lokale Änderungen im lokalen Submodulverzeichnis haben, während das Submodul aktualisiert wird.

In den meisten Fällen ist die lokale Änderung möglicherweise nicht diejenige, die Sie festschreiben möchten. Dies kann aufgrund eines Löschens einer Datei in Ihrem Submodul usw. geschehen. Wenn dies der Fall ist, führen Sie einen Reset in Ihrem lokalen Submodulverzeichnis und in Ihrem übergeordneten Projektverzeichnis erneut aus:

git submodule update --init --recursive

5
Das ist die wahre Antwort. Kann ich es irgendwie in mein Remote-Repository verschieben?
MonsterMMORPG

Dies funktioniert für neue Submodule! Ich könnte alle anderen aktualisieren, aber der Ordner mit den neuen Submodulen bleibt leer, bis ich diesen Befehl ausführe.
Alexis Wilke

1
Es werden keine Änderungen für vorhandene Submodule vorgenommen
Sergey G.

73

Ihr Hauptprojekt weist auf ein bestimmtes Commit hin, bei dem sich das Submodul befinden sollte. git submodule updateversucht, dieses Commit in jedem initialisierten Submodul zu überprüfen. Das Submodul ist wirklich ein unabhängiges Repository - nur ein neues Commit im Submodul zu erstellen und das zu pushen ist nicht genug. Sie müssen auch die neue Version des Submoduls explizit im Hauptprojekt hinzufügen.

In Ihrem Fall sollten Sie also das richtige Commit im Submodul finden - nehmen wir an, das ist der Tipp von master:

cd mod
git checkout master
git pull origin master

Kehren Sie nun zum Hauptprojekt zurück, stellen Sie das Submodul bereit und legen Sie Folgendes fest:

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

Schieben Sie nun Ihre neue Version des Hauptprojekts:

git push origin master

Wenn ab diesem Zeitpunkt jemand anderes sein Hauptprojekt git submodule updateaktualisiert, aktualisiert er für ihn das Submodul, sofern es initialisiert wurde.


24

Es scheint, als würden in dieser Diskussion zwei verschiedene Szenarien miteinander vermischt:

Szenario 1

Mit den Zeigern meines übergeordneten Repositorys auf Submodule möchte ich das Commit in jedem Submodul überprüfen, auf das das übergeordnete Repository verweist, möglicherweise nachdem ich zuerst alle Submodule durchlaufen und diese von Remote aktualisiert / abgerufen habe.

Dies ist, wie bereits erwähnt, erledigt

git submodule foreach git pull origin BRANCH
git submodule update

Szenario 2, das meiner Meinung nach das Ziel von OP ist

In einem oder mehreren Submodulen sind neue Dinge passiert, und ich möchte 1) diese Änderungen übernehmen und 2) das übergeordnete Repository so aktualisieren, dass es auf das (neueste) HEAD-Commit dieses / dieser Submodule verweist.

Dies würde von erledigt werden

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

Nicht sehr praktisch, da Sie n Pfade zu allen n Submodulen in z. B. einem Skript fest codieren müssten, um die Festschreibungszeiger des übergeordneten Repositorys zu aktualisieren.

Es wäre cool, eine automatisierte Iteration durch jedes Submodul zu haben und den übergeordneten Repository-Zeiger (mit git add) zu aktualisieren , um auf den Kopf des Submoduls (der Submodule) zu zeigen.

Dafür habe ich dieses kleine Bash-Skript erstellt:

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

Führen Sie es aus, um es auszuführen

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

Ausarbeitung

Zunächst gehe ich davon aus, dass der Zweig mit dem Namen $ BRANCH (zweites Argument) in allen Repositorys vorhanden ist. Fühlen Sie sich frei, dies noch komplexer zu machen.

In den ersten Abschnitten wird überprüft, ob die Argumente vorhanden sind. Dann ziehe ich die neuesten Inhalte des übergeordneten Repositorys ab (ich bevorzuge es, --ff (Schnellvorlauf) zu verwenden, wenn ich nur Pulls mache. Ich habe Rebase Off, BTW).

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

Dann kann eine Submodulinitialisierung erforderlich sein, wenn neue Submodule hinzugefügt wurden oder noch nicht initialisiert wurden:

git submodule sync
git submodule init
git submodule update

Dann aktualisiere / ziehe ich alle Submodule:

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

Beachten Sie einige Dinge: Zunächst verkette ich einige Git-Befehle mit &&- was bedeutet, dass der vorherige Befehl fehlerfrei ausgeführt werden muss.

Nach einem möglichen erfolgreichen Pull (wenn neue Inhalte auf der Fernbedienung gefunden wurden) mache ich einen Push, um sicherzustellen, dass ein mögliches Merge-Commit nicht auf dem Client zurückbleibt. Wiederum passiert es nur, wenn ein Zug tatsächlich neue Sachen einbringt.

Schließlich stellt das Finale || truesicher, dass das Skript bei Fehlern fortgesetzt wird. Damit dies funktioniert, muss alles in der Iteration in doppelte Anführungszeichen gesetzt werden, und die Git-Befehle werden in Klammern gesetzt (Operatorpriorität).

Mein Lieblingsabschnitt:

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

Iterieren Sie alle Submodule - mit --quiet, wodurch die Ausgabe 'Eingabe von MODULE_PATH' entfernt wird. Mit 'echo $path'(muss in einfachen Anführungszeichen stehen) wird der Pfad zum Submodul in die Ausgabe geschrieben.

Diese Liste der relativen Submodulpfade wird in einem Array ( $(...)) erfasst. Wiederholen Sie dies schließlich git add $i, um das übergeordnete Repository zu aktualisieren.

Schließlich ein Commit mit einer Meldung, die erklärt, dass das übergeordnete Repository aktualisiert wurde. Dieses Festschreiben wird standardmäßig ignoriert, wenn nichts unternommen wurde. Schieben Sie dies zum Ursprung, und Sie sind fertig.

Ich habe ein Skript, das dies in einem Jenkins- Job ausführt, das anschließend mit einer geplanten automatisierten Bereitstellung verknüpft wird, und es funktioniert wie ein Zauber.

Ich hoffe, das wird jemandem helfen.


2
! @ # $% SO Wir verwenden Skripte, die Ihren ähnlich sind. eine Anmerkung: Anstelle von "git submodule foreach --quiet" echo $ path "" verwenden wir "git submodule foreach --recursive --quiet pwd" innerhalb der for-Schleifen. Der pwdBefehl gibt den richtigen 'absoluten Pfad' für jedes vorhandene Submodul aus. --recursivestellt sicher, dass wir alle Submodule besuchen , einschließlich der Submodule innerhalb der Submodule, die in einem großen Projekt vorhanden sein können. Beide Methoden verursachen Probleme mit Verzeichnissen, die Leerzeichen enthalten. /c/Users/Ger/Project\ Files/...Daher wird in unseren Projekten niemals Leerzeichen verwendet.
Ger Hobbelt

2
Das ist schön, und Sie haben Recht, dass es in einigen Antworten ein Missverständnis darüber gibt, was die Frage überhaupt ist, aber wie die ausgezeichnete Antwort von David Z zeigt, ist Ihr Skript nicht erforderlich, da die Funktionalität seit Mitte 2013 in Git integriert ist Sie haben die --remoteOption hinzugefügt . git submodule update --remoteverhält sich ungefähr so ​​wie Ihr Skript.
Mark Amery

@GerHobbelt Danke. Sie haben Recht, wir haben nur eine Ebene von Submodulen, also hätte ich nie gedacht, sie rekursiv zu machen. Ich werde das Skript nicht aktualisieren, bevor ich die Möglichkeit hatte, zu überprüfen, ob es wie erwartet funktioniert, aber definitiv würde mein Skript Sub-Sub-Module enthalten. Leerzeichen in Ordnern klingen definitiv nach etwas, das man vermeiden sollte! : S
Frederik Struck-Schøning

@ MarkAmery Danke für dein Feedback. Ich sehe jedoch ein Problem: Kein Argument, das in der Lage ist, einen Zweig für Submodule anzugeben. Aus dem Git-Handbuch: 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).Ich möchte weder .gitmodules noch .git / config jedes Mal bearbeiten, wenn ich dies für einen anderen Zweig als master tun möchte. Aber vielleicht habe ich etwas verpasst? Außerdem scheint die Methode rekursive Zusammenführungen zu erzwingen (wodurch die Möglichkeit eines schnellen Vorlaufs fehlt).
Frederik Struck-Schøning

Letzte Sache: Ich habe die Methode von @ DavidZ ausprobiert, und sie scheint nicht genau das zu tun, was ich mir vorgenommen habe (und nach welcher Operation gefragt wurde): Hinzufügen des HEAD-Commits von Submodulen zum übergeordneten Element (dh "Aktualisieren des Zeigers") ). Es scheint jedoch die einzige Aufgabe sehr gut (und schneller) zu erfüllen, die neuesten Änderungen in allen Submodulen abzurufen und zusammenzuführen. Leider standardmäßig nur vom Hauptzweig aus (es sei denn, Sie bearbeiten die .gitmodules-Datei (siehe oben)).
Frederik Struck-Schøning

19

Schlicht und einfach, um die Submodule zu holen:

git submodule update --init --recursive

Und jetzt aktualisieren Sie sie auf den neuesten Hauptzweig (zum Beispiel):

git submodule foreach git pull origin master

12

Beachten Sie, dass die moderne Form der Aktualisierung von Submodul-Commits Folgendes wäre:

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

Die ältere Form war:

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

Außer ... diese zweite Form ist nicht wirklich "leise".

Siehe Commit a282f5a (12. April 2019) von Nguyễn Thái Ngọc Duy ( pclouds) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit f1c9f6c , 25. April 2019)

submodule foreach: fix " <command> --quiet" wird nicht respektiert

Robin hat das gemeldet

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

ist nicht mehr wirklich leise.
Es sollte vor fc1b924 ( submodule: Port- submoduleUnterbefehl ' foreach' von Shell zu C, 2018-05-10, Git v2.19.0-rc0) ruhig sein, da parseoptdann keine Optionen versehentlich gegessen werden können.

" git pull" verhält sich wie --quietnicht gegeben.

Dies geschieht, weil parseoptin submodule--helperversucht, beide --quietOptionen so zu analysieren , als wären sie foreachs Optionen, nicht git-pull's.
Die analysierten Optionen werden aus der Befehlszeile entfernt. Wenn wir später ziehen, führen wir genau dies aus

git pull origin

Wenn Sie den Submodul-Helfer aufrufen, wird das Hinzufügen von " --" vor " git pull" beendet, um parseoptOptionen zu analysieren, die nicht wirklich dazu gehören submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWNwird aus Sicherheitsgründen entfernt. parseoptsollte niemals unbekannte Optionen sehen oder etwas ist schief gelaufen. Es gibt auch ein paar Verwendungszeichenfolgen-Updates, während ich sie mir ansehe.

Währenddessen füge ich auch --anderen Unterbefehlen hinzu, die $@an übergeben werden submodule--helper. " $@" in diesen Fällen sind Pfade und weniger wahrscheinlich --something-like-this.
Aber der Punkt bleibt bestehen, git-submodulehat analysiert und klassifiziert, was Optionen sind, was Pfade sind.
submodule--helperÜbergebene Pfade sollten niemals als git-submoduleOptionen betrachtet werden, selbst wenn sie wie eine aussehen.


Und Git 2.23 (Q3 2019) behebt ein weiteres Problem: " git submodule foreach" hat die an den Befehl übergebenen Befehlszeilenoptionen nicht geschützt, damit sie in jedem Submodul korrekt ausgeführt werden, wenn die --recursiveOption " " verwendet wurde.

Siehe Commit 30db18b (24. Juni 2019) von Morian Sonnet ( momoson) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 968eecb , 09. Juli 2019)

submodule foreach: Rekursion von Optionen korrigieren

Berufung:

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

führt zu einem Fehler, der besagt, dass die Option --<option> unbekannt ist submodule--helper.
Das ist natürlich nur dann, wenn <option>keine gültige Option für ist git submodule foreach.

Der Grund dafür ist, dass der obige Aufruf intern in einen Aufruf an submodule - helper übersetzt wird:

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

Dieser Aufruf beginnt mit der Ausführung des Unterbefehls mit seiner Option innerhalb des Submoduls der ersten Ebene und wird mit dem Aufruf der nächsten Iteration des fortgesetzt submodule foreach Aufrufs

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

innerhalb des Submoduls der ersten Ebene. Beachten Sie, dass der doppelte Bindestrich vor dem Unterbefehl fehlt.

Dieses Problem tritt erst seit kurzem auf, da das PARSE_OPT_KEEP_UNKNOWNFlag für die Argumentanalyse von git submodule foreachin commit entfernt wurde a282f5a entfernt wurde .
Daher wird die unbekannte Option jetzt beanstandet, da die Argumentanalyse durch den Doppelstrich nicht ordnungsgemäß beendet wird.

Dieses Commit behebt das Problem, indem während der Rekursion der doppelte Bindestrich vor dem Unterbefehl hinzugefügt wird.


7
git pull --recurse-submodules

Dadurch werden die neuesten Commits abgerufen.


4

In meinem Fall wollte ich gitauf den neuesten Stand aktualisieren und gleichzeitig alle fehlenden Dateien neu füllen.

Im Folgenden wurden die fehlenden Dateien wiederhergestellt (dank --forcederer hier anscheinend nichts erwähnt wurde), aber es wurden keine neuen Commits gezogen:

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

Dies tat:

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


3

@ Jason ist in gewisser Weise korrekt, aber nicht ganz.

aktualisieren

Aktualisieren Sie die registrierten Submodule, dh klonen Sie fehlende Submodule und checken Sie das im Index des enthaltenen Repositorys angegebene Commit aus. Dadurch werden die Submodule HEAD getrennt, es sei denn, --rebase oder --merge ist angegeben oder das Schlüssel-Submodul. $ Name.update ist auf Rebase oder Merge eingestellt.

Also, git submodule updatecheckout, aber es ist das Commit im Index des enthaltenen Repositorys. Das neue Commit ist noch gar nicht bekannt. Gehen Sie also zu Ihrem Submodul, holen Sie sich das gewünschte Commit und schreiben Sie den aktualisierten Submodulstatus im Haupt-Repository fest. Führen Sie dann das aus git submodule update.


1
Wenn ich das Submodul auf ein anderes Commit verschiebe und dann ausführe git submodule update, verschiebt update das Submodul auf das Commit, das im aktuellen HEAD des Superprojekts angegeben ist. (Was auch immer das letzte Commit im Superprojekt besagt, dass das Teilprojekt sein sollte - dieses Verhalten erscheint mir nach der Erklärung in Jasons Beitrag logisch) Es scheint auch abzurufen, aber nur für den Fall, dass sich das Teilprojekt auf dem falschen Commit befindet , was zu meiner Verwirrung beitrug.
Thanatos

2

Hier ist ein großartiger Einzeiler, mit dem Sie alles auf den neuesten Stand bringen können:

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

Vielen Dank an Mark Jaquith


2

Wenn Sie den Host-Zweig nicht kennen, gehen Sie wie folgt vor:

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

Es wird ein Zweig des Haupt-Git-Repositorys abgerufen und dann für jedes Submodul derselbe Zweig abgerufen.


0

Wenn Sie den masterZweig für jedes Submodul auschecken möchten, können Sie zu diesem Zweck den folgenden Befehl verwenden:

git submodule foreach git checkout master
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.