Обновите подмодуль Git для последнего фиксации по происхождению

У меня есть проект с подмодулем Git. Это из URL-адреса ssh://... и находится на фиксации A. Commit B был нажат на этот URL-адрес, и я хочу, чтобы подмодуль извлекал фиксацию и менял ее.

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

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://[email protected]/git/mod mod
Cloning into mod...
[email protected] 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://[email protected]/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://[email protected]/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 -удалением модуля и повторным добавлением его, но в принципе это и неправильно, и утомительно на практике.

Ответ 1

Команда 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

Ответ 2

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

git submodule update --remote --merge

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

--remote

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

Это эквивалентно запуску git pull в каждом подмодуле, что обычно именно то, что вам нужно.

Ответ 3

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

git submodule update --init

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

git submodule update --init --recursive

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

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

git submodule update --init --recursive

Ответ 4

Ваш основной проект указывает на конкретный коммит, в котором должен быть подмодуль. 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 для них обновит подмодуль, предполагая, что он был инициализирован.

Ответ 5

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

Сценарий 1

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

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

git submodule foreach git pull origin BRANCH
git submodule update

Сценарий 2, на который, я думаю, нацелен OP

Новые вещи произошли в одном или нескольких подмодулях, и я хочу 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 (быстрая перемотка вперед) всякий раз, когда я просто выполняю вытягивание. У меня есть ребаз, BTW).

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

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

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

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

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

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

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

Я надеюсь, что это поможет кому-то.

Ответ 6

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

git submodule update --init --recursive

А теперь перейдите к обновлению до последней ветки master (например):

git submodule foreach git pull origin master

Ответ 7

Обратите внимание, что современная форма обновления коммитов подмодулей будет выглядеть так:

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

Старая форма была:

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

За исключением... эта вторая форма не совсем "тихая".

См. коммит a282f5a (12 апреля 2019 г.) от Нгуен Тай Нгок Дуй (pclouds).
(Merged by Junio C Hamano -- [TG43] -- in commit f1c9f6c, 25 Apr 2019)

submodule foreach: исправьте несоответствие "<command> --quiet"

Робин сообщил, что

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

больше не совсем тихо.
Перед fc1b924 должно быть тихо (submodule: подкоманда port submodule 'foreach' из оболочки в C, 2018-05-10, Git v2.19.0-rc0), потому что parseopt может не случайно есть варианты.

"git pull" ведет себя так, как будто --quiet не указан.

Это происходит потому, что parseopt в submodule--helper попытается проанализировать обе опции --quiet, как если бы они были опциями foreach, а не git-pull.
Проанализированные параметры удаляются из командной строки. Итак, когда мы делаем потяните позже, мы выполним только это

git pull origin

При вызове помощника субмодуля добавление "--" перед "git pull" приведет к остановите parseopt для анализа параметров, которые на самом деле не принадлежат submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWN удаляется в качестве меры безопасности. parseopt следует никогда не вижу неизвестных вариантов или что-то пошло не так. Это также обновление строки использования пары, пока я смотрю на них.

При этом я также добавляю "--" к другим подкомандам, которые передают "[email protected]" submodule--helper. "[email protected]" в этих случаях являются путями и менее вероятны --something-like-this.
Но вопрос остается в силе, git-submodule проанализировал и классифицировал, какие есть варианты, какие есть пути.
submodule--helper никогда не должен рассматривать пути, пройденные git-submodule, как опции, даже если они выглядят как один.


А Git 2.23 (Q3 2019) исправляет еще одну проблему: "git submodule foreach" не защищал параметры командной строки, передаваемые команде для правильного запуска в каждом подмодуле, когда использовалась опция "--recursive".

Смотрите коммит 30db18b (24 июня 2019 г.) от Morian Sonnet (momoson).
(Merged by Junio C Hamano -- [TG435] -- in commit 968eecb, 09 Jul 2019)

submodule foreach: исправить рекурсию опций

Calling:

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

приводит к ошибке, утверждающей, что опция --<option> неизвестна submodule--helper.
Это, конечно, только тогда, когда <option> не является допустимым параметром для git submodule foreach.

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

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

Этот вызов начинается с выполнения подкоманды с ее опцией внутри подмодуль первого уровня и продолжается, вызывая следующую итерацию вызов submodule foreach

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

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

Эта проблема начинает возникать только недавно, поскольку флаг PARSE_OPT_KEEP_UNKNOWN для разбора аргумента git submodule foreach был удален в коммите a282f5a.
Следовательно, неизвестная опция теперь жаловалась, так как разбор аргументов не заканчивается должным образом двойной чертой.

Эта фиксация устраняет проблему, добавляя двойную черту перед подкомандой во время рекурсии.

Ответ 8

git pull --recurse-submodules

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

Ответ 9

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

Обновить

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

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

Ответ 10

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

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

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

Это сделал:

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

Ответ 11

Вот потрясающий однострочный файл, чтобы обновить все до мастера:

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

Спасибо Mark Jaquith

Ответ 12

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

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

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