1141 Stimmen

Git-Submodul auf die letzte Übergabe auf Origin aktualisieren

Ich habe ein Projekt mit einem Git-Submodul. Es ist von einer ssh://... URL und befindet sich auf Commit A. Commit B wurde zu dieser URL gepusht, und ich möchte, dass das Submodul den Commit abruft und zu ihm wechselt.

Ich gehe davon aus, dass git submodule update sollte dies tun, tut es aber nicht. Es tut sich nichts (keine Ausgabe, kein 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 scheinbar einen Fetch durchführt (was aber nicht möglich ist, da es nicht nach einem Passwort fragt), aber git log y git show die Existenz neuer Commits leugnen. Bis jetzt habe ich nur rm -Modul zu entfernen und neu hinzuzufügen, aber das ist sowohl im Prinzip falsch als auch in der Praxis mühsam.

30voto

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

Szenario 1

Mit den Zeigern meines übergeordneten Repositorys auf die Submodule möchte ich die Übergabe in jedem Submodul, auf das das übergeordnete Repository zeigt, überprüfen, möglicherweise nachdem ich zuerst alle Submodule durchlaufen und diese aus der Ferne aktualisiert/abgerufen habe.

Dies geschieht, wie bereits erwähnt, mit

git submodule foreach git pull origin BRANCH
git submodule update

Szenario 2, auf das OP meiner Meinung nach abzielt

In einem oder mehreren Untermodulen hat sich etwas getan, und ich möchte 1) diese Änderungen übernehmen und 2) das übergeordnete Repository aktualisieren, damit es auf den (neuesten) HEAD-Commit dieses/r Untermodule verweist.

Dies würde geschehen durch

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

Das ist nicht sehr praktisch, da man n Pfade zu allen n Untermodulen in einem Skript zur Aktualisierung der Commit-Pointer des übergeordneten Repositorys fest einprogrammieren müsste.

Es wäre toll, wenn man jedes Submodul automatisch durchlaufen könnte, um den Zeiger des übergeordneten Repositorys zu aktualisieren (mit git add ) auf den Kopf des/der Submodule(s) verweisen.

Zu diesem Zweck 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

Um es auszuführen, führen Sie

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

Ausarbeitung

Zunächst einmal gehe ich davon aus, dass der Zweig mit dem Namen $BRANCH (zweites Argument) in allen Repositories existiert. Sie können dies gerne noch komplexer gestalten.

In den ersten beiden Abschnitten wird überprüft, ob die Argumente vorhanden sind. Dann ziehe ich das neueste Material des übergeordneten Repositorys (ich bevorzuge --ff (fast-forwarding), wenn ich nur pulls mache. Ich habe übrigens rebase ausgeschaltet).

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

Dann kann es notwendig sein, einige Submodule zu initialisieren, wenn neue Submodule hinzugefügt wurden oder noch nicht initialisiert sind:

git submodule sync
git submodule init
git submodule update

Dann aktualisiere/entferne ich alle Untermodule:

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

Beachten Sie ein paar Dinge: Zunächst einmal kette ich einige Git-Befehle mit && - was bedeutet, dass der vorherige Befehl ohne Fehler ausgeführt werden muss.

Nach einem möglicherweise erfolgreichen Pull (wenn auf der Gegenseite neue Dinge gefunden wurden), mache ich einen Push, um sicherzustellen, dass ein möglicher Merge-Commit nicht auf dem Client zurückbleibt. Wiederum geschieht dies nur wenn ein Pull hat tatsächlich neue Sachen eingebracht.

Schließlich ist die letzte || true stellt sicher, 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 (Vorrang der Operatoren).

Mein Lieblingsteil:

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

Iterieren Sie alle Untermodule - mit --quiet , wodurch die Ausgabe 'Entering MODULE_PATH' entfernt wird. Verwendung von '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 ( $(...) ) - schließlich wiederholen Sie dies und machen git add $i um das übergeordnete Repository zu aktualisieren.

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

Ich habe ein Skript, das dies in einer Jenkins Job, der anschließend an eine geplante automatische Bereitstellung gekoppelt ist, und es funktioniert wunderbar.

Ich hoffe, dass dies jemandem helfen wird.

23voto

Schlicht und einfach, um die Untermodule zu holen:

git submodule update --init --recursive

Und nun aktualisieren Sie sie auf den neuesten Master-Zweig (zum Beispiel):

git submodule foreach git pull origin master

11voto

git pull --recurse-submodules

Dadurch werden alle aktuellen Commits gezogen.

9voto

Jobin James Punkte 778

Das funktioniert bei mir, um auf die neuesten Übertragungen zu aktualisieren

git submodule update --recursive --remote --init

4voto

manojlds Punkte 273771

@Jason hat in gewisser Weise recht, aber nicht ganz.

Update

Aktualisieren Sie die registrierten Submodule, d.h. Klonen von fehlenden Submodulen und checkt die Übergabe aus, die in der Option Index des enthaltenen Repositorys angegebene Übergabe. Dadurch werden die Submodule HEAD abgetrennt, es sei denn --rebase oder --merge ist angegeben ist oder der Schlüssel submodule.$name.update auf rebase oder merge.

Also, git submodule update checkt zwar aus, aber nur auf die Übergabe im Index des enthaltenen Repositorys. Es kennt den neuen Commit im Upstream noch gar nicht. Gehen Sie also zu Ihrem Submodul, holen Sie den gewünschten Commit und übertragen Sie den aktualisierten Submodulstatus in das Haupt-Repository und führen Sie dann den git submodule update .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X