Обновите подмодуль Git до последней фиксации на источнике

У меня есть проект с подмодулем 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- добавление модуля и его повторное добавление, но в принципе это неправильно и утомительно на практике.

17 ответов

Решение

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

Git 1.8.2 имеет новую опцию --remote это позволит именно это поведение. Бег

git submodule update --remote --merge

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

--дистанционный пульт

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

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

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

git submodule update --init 

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

git submodule update --init --recursive

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

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

git submodule update --init --recursive 

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

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

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

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

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

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

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

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

См. Коммит а282f5a (12 апреля 2019 г.) Нгуен Тхай Нгук Дуй ( pclouds )
(Объединено Юнио С Хамано - gitster - в коммите f1c9f6c, 25 апреля 2019 г.)

submodule foreach: fix " <command> --quiet "не соблюдая

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

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

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

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

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

git pull origin

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

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

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

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

Сценарий 1

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

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

git submodule foreach git pull origin BRANCH
git submodule update

Сценарий 2, на который, как мне кажется, нацелен OP

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

Первая пара разделов - это проверка наличия аргументов. Затем я извлекаю последние материалы родительского репо (я предпочитаю использовать --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, который удаляет вывод 'Entering MODULE_PATH'. С помощью 'echo $path' (должен быть в одинарных кавычках), путь к подмодулю записывается на выход.

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

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

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

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

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

git submodule update --init --recursive

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

git submodule foreach git pull origin master

Как обновить все подмодули git в репозитории (два способа сделать две совершенно разные вещи!)

Краткое резюме

      # Option 1: as a **user** of the outer repo, pull the latest changes of the
# sub-repos as previously specified (pointed to as commit hashes) by developers
# of this outer repo.
# - This recursively updates all git submodules to their commit hash pointers as
#   currently committed in the outer repo.
git submodule update --init --recursive

# Option 2. As a **developer** of the outer repo, update all subrepos to force
# them each to pull the latest changes from their respective upstreams (ex: via
# `git pull origin main` or `git pull origin master`, or similar, for each
# sub-repo). 
git submodule update --init --recursive --remote

# For both options above: now add and commit these subrepo changes
git add -A
git commit -m "Update all subrepos to their latest upstream changes"

Подробности

  1. Вариант 1: как пользователь внешнего репозитория, пытающийся привести все подмодули в состояние, задуманное разработчиками внешнего репо:
            git submodule update --init --recursive
    
  2. Вариант 2: как разработчик внешнего репозитория, он пытается обновить все подмодули до последней фиксации, отправленной в ветку по умолчанию каждого из их удаленных репозиториев (т. е. обновить все подрепозитории до последнего состояния, задуманного разработчиками каждого подрепозитория):
            git submodule update --init --recursive --remote
    
    ... вместо использования или .

Мне кажется, что лучший ответ для обоих вариантов выше — не использовать--mergeи--forceварианты, которые я вижу в некоторых других ответах.

Объяснение опций, использованных выше:

  • в--initчасть выше инициализирует подмодуль, если вы только что клонировали репо и еще этого не сделали
  • --recursiveделает это для подмодулей внутри подмодулей, рекурсивно вниз навсегда
  • и предлагает обновить подмодуль до последней фиксации в ветке по умолчанию на удаленном по умолчанию для подмодуля. Это как делать или в большинстве случаев, например, для каждого подмодуля. Если вы хотите вместо этого обновить фиксацию, указанную в самом внешнем репо (суперрепо), остановитесь.

(не используйте это - это часто терпит неудачу) противgit submodule update --recursive --remote(используйте это! - это всегда работает)

Я оставил следующие комментарии . Я думаю, что они важны, поэтому я тоже добавляю их в свой ответ.

В принципе, для некоторых ситуацийgit submodule foreach --recursive git pullможет работать. Для других,git submodule foreach --recursive git pull origin masterможет быть то, что вам нужно вместо этого. Для других,git submodule foreach --recursive git pull origin mainможет быть то, что вам нужно. А для других все еще ничего из этого не может работать! Вам может понадобитьсяgit submodule foreach --recursive git pull upstream develop, например. ИЛИ, что еще хуже, может не быть какой-либо команды, которая работает для вашего внешнего репо, поскольку каждому подмодулю может потребоваться другая команда для обновления себя из своей удаленной и стандартной ветки по умолчанию. Однако во всех случаях, которые я могу найти, это работает , в том числе во всех случаях, когда вы можете использовать один из несколькихgit submodule foreachкоманды, которые я только что представил выше. Итак, используйте это вместо:

      git submodule update --recursive --remote

В любом случае, вот несколько моих комментариев по этому поводу под этим ответомпод этим ответом :

(1/4) @DavidZ, многие люди думают, что это одно и то же, а последнее просто является более новой командой. Однако это не одно и то же. потерпит неудачу при нескольких обстоятельствах , для которых работает просто отлично! Если ваш подмодуль указывает на хэш коммита, на который не указывает ветка, что часто имеет место в реальной разработке, когда вам нужна определенная версия подмодуля для вашего внешнего репо, тогда этот подмодуль...

(2/4)... находится в отключенном состоянии HEAD. В этом случае,git submodule foreach git pullне запускается в этом подмодуле, так как отдельный HEAD не может иметь вышестоящую ветвь. , однако, работает просто отлично! Кажется, он вызывает этот подмодуль, если он является удаленным по умолчанию и является ветвью по умолчанию на этом удаленном по умолчанию, или, например, еслиoriginявляется удаленным по умолчанию, но является ветвью по умолчанию.

(3/4) Кроме того,git submodule foreach git pull origin masterбудет даже терпеть неудачу во многих случаях, когда работает просто отлично, поскольку многие подмодули используют в качестве ветки по умолчанию, а многие другие подмодули используют в качестве ветки по умолчанию, поскольку GitHub изменился сmasterкmainв последнее время для того, чтобы уйти от терминов, связанных с рабством в США («хозяин» и «раб»).

(4/4) Итак, я добавил явный удаленный доступ и ветку, чтобы было понятнее, что они часто нужны, и чтобы напомнить людям, чтоgit pullчасто недостаточно иgit pull origin masterможет и не работатьgit pull origin mainможет работать, когда первый не работает, но также может и не работать, и что ни один из них сам по себе не является таким же, какgit submodule update --remote, так как эта последняя команда достаточно умна, чтобы просто сделатьgit pull <default_remote> <default_branch>для вас для каждого субмодуля, видимо, настраивая пульт и ветку по мере необходимости для каждого субмодуля.

Связанные и другие исследования

  1. Как найти основную ветку репо: /questions/25346831/git-kak-poluchit-vetku-po-umolchaniyu/25346844#25346844
  2. Как обновить каждое подрепозиторий, запустив в нем пользовательскую команду черезgit submodule foreach <cmd>: /questions/12742234/obnovite-podmodul-git-do-poslednej-fiksatsii-na-istochnike/12742272#12742272
  3. man git submodule- тогда ищитеforeach,--remote, и т. д.
  4. Мой ответ о том, как разрешать конфликты с подмодулями git, в вашем внешнем репо, содержащем их
git pull --recurse-submodules

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

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

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

Обновить

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

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

Если вы хотите оформить заказ master ветвь для каждого подмодуля - для этой цели вы можете использовать следующую команду:

git submodule foreach git checkout master

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

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

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

Это сделал:

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

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

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

Он получит ветку основного git, а затем для каждого подмодуля сделает тянуть ту же ветку

Для меня все git submoduleничего не работает. Но это сработало:

      cd <path/to/submodule>
git pull

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

      cd <path/to/repo>
git commit -m "update latest version" <relative_path/to/submodule>
git push

который обновляет ваше удаленное репо (со ссылкой на последний коммит repo@xxxxxx).

Самый простой способ обрабатывать проекты git, содержащие подмодули, - всегда добавлять

--recurse-submodules 

в конце каждого примера команды git:

git fetch --recurse-submodules

еще один

git pull --update --recurse-submodules

и т.д...

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

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

Благодаря Марку Хакиту

Другие вопросы по тегам