Обновите подмодуль 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: как пользователь внешнего репозитория, пытающийся привести все подмодули в состояние, задуманное разработчиками внешнего репо:
git submodule update --init --recursive
- Вариант 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>
для вас для каждого субмодуля, видимо, настраивая пульт и ветку по мере необходимости для каждого субмодуля.
Связанные и другие исследования
- Как найти основную ветку репо: /questions/25346831/git-kak-poluchit-vetku-po-umolchaniyu/25346844#25346844
- Как обновить каждое подрепозиторий, запустив в нем пользовательскую команду через
git submodule foreach <cmd>
: /questions/12742234/obnovite-podmodul-git-do-poslednej-fiksatsii-na-istochnike/12742272#12742272 -
man git submodule
- тогда ищитеforeach
,--remote
, и т. д. - Мой ответ о том, как разрешать конфликты с подмодулями git, в вашем внешнем репо, содержащем их
git pull --recurse-submodules
Это потянет все последние коммиты.
@ Джейсон прав в некотором смысле, но не совсем.
Обновить
Обновите зарегистрированные подмодули, то есть клонируйте отсутствующие подмодули и извлеките фиксацию, указанную в индексе репозитория. Это приведет к отсоединению подмодулей 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