Обновите подмодуль Git до последней фиксации на источнике - PullRequest
737 голосов
/ 29 апреля 2011

У меня есть проект с подмодулем Git.Он находится на URL-адресе ssh: // ... и находится в коммите A. Коммит B был отправлен на этот URL, и я хочу, чтобы субмодуль получил коммит и изменил его.

ТеперьНасколько я понимаю, git submodule update должен сделать это, но это не так.Он ничего не делает (нет вывода, код успешного завершения).Вот пример:

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

Я также попробовал git fetch mod, который, кажется, делает выборку (но не может, потому что не запрашивает пароль!), Но git log иgit show отрицать существование новых коммитов.До сих пор я только что rm -обработал модуль и заново его добавил, но на практике это неправильно и утомительно.

Ответы [ 12 ]

1271 голосов
/ 29 апреля 2011

Команда git submodule update фактически говорит Git, что вы хотите, чтобы ваши подмодули проверяли каждый коммит, уже указанный в индексе суперпроекта. Если вы хотите обновить ваших подмодулей до последнего коммита, доступного с их пульта, вам нужно будет сделать это прямо в подмодулях.

Итак, в итоге:

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

Или, если вы занятой человек:

git submodule foreach git pull origin master
387 голосов
/ 18 января 2014

Git 1.8.2 имеет новую опцию --remote, которая включит именно это поведение.Выполнение

git submodule update --remote --merge

извлечет последние изменения из апстрима в каждом подмодуле, объединит их и проверит последнюю версию подмодуля.Как указано в , в документации указано:

- remote

Эта опция действительна только для команды обновления.Вместо того, чтобы использовать записанный SHA-1 суперпроекта для обновления подмодуля, используйте состояние ветви удаленного отслеживания подмодуля.

Это эквивалентно выполнению git pull в каждом подмодуле, что обычно точночто ты хочешь.

108 голосов
/ 18 июня 2013

В родительском каталоге вашего проекта выполните:

git submodule update --init

Или, если у вас рекурсивные подмодули, запустите:

git submodule update --init --recursive

Иногда это по-прежнему не работает, потому что у вас есть локальные изменения в локальном каталоге подмодуля во время обновления подмодуля.

В большинстве случаев локальные изменения могут быть не теми, которые вы хотите зафиксировать. Это может произойти из-за удаления файла в вашем подмодуле и т. Д. Если это так, выполните сброс в локальном каталоге подмодуля и в родительском каталоге вашего проекта, запустите снова:

git submodule update --init --recursive
70 голосов
/ 29 апреля 2011

Ваш основной проект указывает на конкретный коммит, в котором должен быть подмодуль.git submodule update пытается проверить этот коммит в каждом подмодуле, который был инициализирован.Подмодуль на самом деле является независимым репозиторием - просто создайте новый коммит в подмодуле и нажмите, что недостаточно.Вам также необходимо явно добавить новую версию подмодуля в основной проект.

Итак, в вашем случае вы должны найти правильный коммит в подмодуле - предположим, что это подсказка master:

cd mod
git checkout master
git pull origin master

Теперь вернитесь к основному проекту, скомпонуйте подмодуль и зафиксируйте следующее:

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

Теперь вставьте новую версию основного проекта:

git push origin master

С этого момента, если кто-то еще обновит свой основной проект, git submodule update для них обновит подмодуль, предполагая, что он был инициализирован.

21 голосов
/ 14 января 2014

Кажется, что в этом обсуждении смешиваются два разных сценария:

Сценарий 1

Используя указатели моего родительского репозитория на подмодули, я хочу проверить фиксацию в каждом подмодуле, на который указывает родительский репозиторий, возможно, после первой итерации всех подмодулей и обновления / извлечения их из удаленного модуля.

Это, как указано, сделано с

git submodule foreach git pull origin BRANCH
git submodule update

Сценарий 2, к которому, как мне кажется, ОП стремится

Новые вещи произошли в одном или нескольких подмодулях, и я хочу 1) вытащить эти изменения и 2) обновить родительский репозиторий, чтобы он указывал на HEAD (последний) коммит этого / этих подмодулей.

Это будет сделано

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

Не очень практично, поскольку вам придется жестко задавать n путей ко всем n подмодулям, например, скрипт для обновления указателей коммитов родительского репозитория.

Было бы здорово иметь автоматическую итерацию для каждого подмодуля, обновляя указатель родительского репозитория (используя git add), чтобы он указывал на заголовок подмодуля (ов).

Для этого я сделал небольшой скрипт 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

Чтобы запустить его, выполните

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

Разработка

Прежде всего, я предполагаю, что ветка с именем $ BRANCH (второй аргумент) существует во всех репозиториях. Не стесняйтесь делать это еще сложнее.

Первая пара разделов посвящена проверке наличия аргументов. Затем я извлекаю последние материалы родительского репозитория (я предпочитаю использовать --ff (быстрая перемотка вперед) всякий раз, когда я просто выполняю вытягивание. У меня отключен ребаз, кстати).

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"

Обратите внимание на несколько вещей: во-первых, я объединяю некоторые команды Git с помощью && - то есть предыдущая команда должна выполняться без ошибок.

После возможного успешного извлечения (если новый материал был обнаружен на пульте), я делаю пуш, чтобы убедиться, что возможный коммит-слияние не остался на клиенте. Опять же, это случается только , если действительно приносит новые вещи.

Наконец, последний || true гарантирует, что скрипт продолжит работу при ошибках. Чтобы это работало, все в итерации должно быть заключено в двойные кавычки, а команды Git заключены в скобки (приоритет оператора).

Моя любимая часть:

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

Перебрать все подмодули - с помощью --quiet, который удаляет вывод «Вход в MODULE_PATH». Используя 'echo $path' (должно быть в одинарных кавычках), путь к подмодулю записывается в вывод.

Этот список относительных путей подмодулей содержится в массиве ($(...)) - наконец, выполните итерацию и выполните git add $i, чтобы обновить родительский репозиторий.

Наконец, коммит с некоторым сообщением, объясняющим, что родительский репозиторий был обновлен. Этот коммит будет проигнорирован по умолчанию, если ничего не было сделано. Переместите это в начало координат, и все готово.

У меня есть сценарий, выполняющий его в задании Jenkins , который впоследствии связывается с запланированным автоматическим развертыванием, и он работает как чудо.

Надеюсь, это кому-нибудь поможет.

17 голосов
/ 17 августа 2017

Просто и понятно, чтобы получить подмодули:

git submodule update --init --recursive

А теперь продолжайте обновлять их до последней главной ветки (например):

git submodule foreach git pull origin master
4 голосов
/ 22 января 2019
git pull --recurse-submodules

Это потянет все последние коммиты.

3 голосов
/ 29 апреля 2011

@ Джейсон верен в некотором смысле, но не полностью.

update

Обновление зарегистрированных подмодулей, т.е. клонирование отсутствующих подмодулей и извлечение коммита, указанного в индексе содержащегорепозиторий.Это приведет к отсоединению подмодулей HEAD, если не указаны --rebase или --merge или субмодуль ключа. $ Name.update настроен для перебазирования или слияния.

Итак, git submodule update проверяет, но это для фиксации в индексе содержащего репозитория.Он еще не знает о новом коммите вверх по течению.Перейдите в свой подмодуль, получите нужный коммит и зафиксируйте обновленное состояние подмодуля в главном репозитории, а затем выполните git submodule update.

2 голосов
/ 26 сентября 2018

В моем случае я хотел, чтобы git обновлял до последней версии и в то же время заново заполнял все отсутствующие файлы.

Следующие файлы восстановили отсутствующие файлы (благодаря --force, который, кажется, здесь не упоминался), но он не потянул новые коммиты:

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

Это сделал:

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

1 голос
/ 14 февраля 2019

Если вы не знаете ветку хоста, сделайте следующее:

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

Она получит ветку основного репозитория Git, а затем для каждого подмодуля сделает ветку той же ветки.

...