Простой способ получить последние из всех подмодулей git


1848

Мы используем подмодули git для управления парой крупных проектов, которые зависят от многих других библиотек, которые мы разработали. Каждая библиотека представляет собой отдельный репозиторий, внесенный в зависимый проект в виде подмодуля. Во время разработки мы часто хотим просто взять последнюю версию каждого зависимого подмодуля.

Есть ли в git встроенная команда для этого? Если нет, то как насчет пакетного файла Windows или аналогичного, который может это сделать?


Git-Deep должен помочь с этим.
Мэтью Куриан

9
@Brad Вы хотите обновить свои копии подмодулей до коммитов оборотов, названных в главном проекте; или вы хотите получить последний коммит HEAD из каждого подмодуля? Большинство ответов здесь относятся к первым; многие хотят последнего.
chrisinmtown

Ответы:


2467

Если вы впервые берете репо, вам нужно использовать его в --initпервую очередь:

git submodule update --init --recursive

Для git 1.8.2 или выше --remoteбыла добавлена опция для поддержки обновления до последних подсказок удаленных веток:

git submodule update --recursive --remote

Это дает дополнительное преимущество, заключающееся в уважении к любым веткам «не по умолчанию», указанным в файлах « .gitmodulesили» .git/config(если у вас есть такие ветки , по умолчанию это origin / master, и в этом случае некоторые другие ответы здесь также будут работать).

Для git 1.7.3 или выше вы можете использовать (но ниже приведены некоторые вопросы о том, какое обновление все еще применяется):

git submodule update --recursive

или:

git pull --recurse-submodules

если вы хотите подтянуть свои подмодули к последним коммитам вместо текущего коммита, на который указывает репо.

Смотрите git-submodule (1) для подробностей


299
Вероятно, вы должны использовать в git submodule update --recursiveнастоящее время.
Йенс Кол

38
Улучшение производительности:git submodule foreach "(git checkout master; git pull)&"
Богдан Гусиев

18
update будет обновлять каждый подмодуль до указанной ревизии, а не обновлять его до последней версии для этого репозитория.
Питер ДеВиз

21
Просто добавьте, что вслепую придерживаться origin masterконца этой команды может привести к неожиданным результатам, если некоторые из ваших подмодулей отслеживают другую ветвь или имя местоположения этого конкретного подмодуля. Очевидно для некоторых, но, вероятно, не для всех.
Натан Хорнби,

31
Просто чтобы уточнить для всех. git submodule update --recursiveпроверяет, какая ревизия хранится в родительском репозитории для каждого подмодуля, а затем проверяет эту ревизию в каждом подмодуле. Он НЕ вытягивает последние коммиты для каждого подмодуля. git submodule foreach git pull origin masterили git pull origin master --recurse-submodulesэто то, что вы хотите, если вы намереваетесь обновить каждый подмодуль до последнего из их исходных репозиториев. Только тогда вы получите ожидающие изменения в родительском репо с обновленными хэшами ревизий для подмодулей. Проверьте это, и все хорошо.
Chev

637
git pull --recurse-submodules --jobs=10

Git впервые узнал в 1.8.5.

Пока ошибка не будет устранена, в первый раз вам нужно запустить

git submodule update --init --recursive


29
upvoted, я использую это: псевдоним update_submodules = 'git pull --recurse-submodules && git submodule update'
Стивен C

3
Это работает, если субмодули уже были извлечены хотя бы один раз, но для субмодулей, которые никогда не были извлечены, см. Ответ gahooa ниже.
Мэтт Браун

8
Это подтянет к версии, указанной верхним репо; это не тянет голову. Например, если TopRepo указывает версию 2 позади HEAD для SubRepo, это будет подтягивать SubRepo с той версией, которая позади 2. Другие ответы здесь тянут ГОЛОВУ в SubRepo.
Крис Москини

11
Обратите внимание , что ни один, git pull --recurse-submodulesни git submodule update --recursiveне делает не инициализации вновь добавленного подмодулей. Для их инициализации вам нужно запустить git submodule update --recursive --init. Цитата из руководства : Если субмодуль еще не инициализирован, и вы просто хотите использовать настройку, сохраненную в .gitmodules, вы можете автоматически инициализировать субмодуль с опцией --init.
patryk.beza

1
возможно добавить подсказку git submodule update --recursive --remote которой также обновляются субмодули до удаленной последней версии вместо сохраненного SHA-1.
Ханно С.

387

На init запускается следующая команда:

git submodule update --init --recursive

из каталога git repo лучше всего работает для меня.

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

Разъяснения

git - the base command to perform any git command
    submodule - Inspects, updates and manages submodules.
        update - Update the registered submodules to match what the superproject
        expects by cloning missing submodules and updating the working tree of the
        submodules. The "updating" can be done in several ways depending on command
        line options and the value of submodule.<name>.update configuration variable.
            --init without the explicit init step if you do not intend to customize
            any submodule locations.
            --recursive is specified, this command will recurse into the registered
            submodules, and update any nested submodules within.

После этого вы можете просто запустить:

git submodule update --recursive

из каталога git repo лучше всего работает для меня.

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


10
Да, ответ с наибольшим количеством голосов был лучшим способом сделать это в '09, но теперь это определенно проще и интуитивно понятнее.
Майкл Скотт Катберт

2
@MichaelScottCuthbert спасибо, я уверен, что через 3 года эта команда тоже будет сумасшедшей
abc123

5
Тем не менее, это не извлекает последнюю версию из субмодуля, а только последнюю версию, которую отслеживает родитель.
Натан Осман

4
@NathanOsman - это то, что вы хотите ... в итоге вы получите неработающий код, если не будете следить за ревизиями родителей. Если вы являетесь опекуном родителя, вы можете обновить его и зафиксировать.
abc123

2
Да, но, насколько я понимаю, это не то, чего хотел ОП.
Натан Осман

305

Примечание: это с 2009 года и, возможно, тогда было хорошо, но сейчас есть лучшие варианты.

Мы используем это. Это называется git-pup:

#!/bin/bash
# Exists to fully update the git repo that you are sitting in...

git pull && git submodule init && git submodule update && git submodule status

Просто поместите его в подходящий каталог bin (/ usr / local / bin). Если в Windows вам может понадобиться изменить синтаксис, чтобы заставить его работать :)

Обновить:

В ответ на комментарий оригинального автора о включении всех заголовков всех подмодулей - это хороший вопрос.

Я почти уверен, что gitне имеет команды для этого внутри. Для этого вам нужно будет определить, что HEAD на самом деле для подмодуля. Это может быть так просто, как сказатьmaster это самая современная ветка и т. Д ...

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

  1. проверить git submodule statusналичие «модифицированных» репозиториев. Первый символ выходных строк указывает на это. Если суб-репо изменен, вы НЕ МОЖЕТЕ продолжить.
  2. для каждого репо в списке, перейдите в его каталог и запустите git checkout master && git pull. Проверьте на ошибки.
  3. В конце я предлагаю вам распечатать дисплей для отображения текущего статуса подмодулей - возможно, предложить им добавить все и зафиксировать?

Я хотел бы отметить, что этот стиль не совсем то, для чего были созданы подмодули git. Как правило, вы хотите сказать, что «LibraryX» имеет версию «2.32» и будет оставаться такой, пока я не скажу «обновить».

Это в некотором смысле то, что вы делаете с описанным сценарием, но просто более автоматически. Требуется осторожность!

Обновление 2:

Если вы работаете на платформе Windows, вы можете захотеть использовать Python для реализации скрипта, так как он очень эффективен в этих областях. Если вы используете unix / linux, тогда я предлагаю просто скрипт bash.

Нужны какие-либо разъяснения? Просто оставьте комментарий.


Я не думаю, что это то, что я хочу. Разве это не потянет версию подмодулей, с которой последний раз был завершен супер-проект? Я хочу вытащить головную версию всех подмодулей.
Брэд Робинсон

3
Это прекрасно работает и работает не только для обновления подмодулей, но и для их извлечения в первый раз, если это то, что вам нужно.
Мэтт Браун

Я просто получаю "Нет информации об отслеживании текущей ветви. Пожалуйста, укажите, с какой веткой вы хотите объединиться". Неважно, что я пытаюсь: /
Натан Хорнби

9
Почему бы не создать для него псевдоним? git config --global alias.pup '!git pull && git submodule init && git submodule update && git submodule status'а затем использовать его как git pupбез каких-либо сценариев.
Fracz

Спасибо, по какой-то причине, несмотря на то, что у меня есть git 1.9.1, который я должен был выполнить git submodule initпосле первого извлечения с включенными подмодулями, чтобы все начало работать правильно.
Бен Усман

164

Хенрик на правильном пути. Команда 'foreach' может выполнить любой произвольный скрипт оболочки. Два варианта, чтобы вытащить самый последний может быть,

git submodule foreach git pull origin master

а также,

git submodule foreach /path/to/some/cool/script.sh

Это будет перебирать все инициализированные подмодули и запускать заданные команды.


144

У меня на Windows работает следующее.

git submodule init
git submodule update

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

52
Тем не менее, это единственное, что на этой странице заставило Git вытащить субмодули, когда я впервые проверил репо
theheadofabroom

2
Также можно использовать: обновление подмодуля git --init --recursive (особенно, если речь идет о RestKit из свежего клона)
HCdev

33

редактировать :

В комментариях было указано (по словам philfreo ), что требуется последняя версия. Если есть вложенные подмодули, которые должны быть в их последней версии:

git submodule foreach --recursive git pull

----- устаревший комментарий ниже -----

Разве это не официальный способ сделать это?

git submodule update --init

Я использую это каждый раз. Пока проблем нет.

Редактировать:

Я только что обнаружил, что вы можете использовать:

git submodule foreach --recursive git submodule update --init 

Который также будет рекурсивно тянуть все подмодули, то есть зависимости.


5
Ваш ответ не отвечает на вопрос ОП, но, чтобы сделать то, что вы предложили, вы можете просто сказатьgit submodule update --init --recursive
philfreo

2
Понятно, нужна последняя версия. Ну, это может быть полезно, если есть вложенные подмодули: git submodule foreach --recursive git pull
антитоксический

1
Я не мог заставить что-либо из этого на самом деле загрузить что-либо - "git submodule update --init --recursive" работал для меня как бы то ни было.
BrainSlugs83

33

Так как может случиться так, что веткой по умолчанию ваших подмодулей не является master, вот как я автоматизирую полные обновления подмодулей Git:

git submodule init
git submodule update
git submodule foreach 'git fetch origin; git checkout $(git rev-parse --abbrev-ref HEAD); git reset --hard origin/$(git rev-parse --abbrev-ref HEAD); git submodule update --recursive; git clean -dfx'

из многих ответов на многие вопросы этот работал для меня (2019, ошибка github с определенными хэш-идентификаторами)
philshem

31

Первый раз

Подмодуль Clone и Init

git clone git@github.com:speedovation/kiwi-resources.git resources
git submodule init

Остаток

Во время разработки просто потяните и обновите субмодуль

git pull --recurse-submodules  && git submodule update --recursive

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

git submodule foreach git pull origin master

Предпочтительный способ должен быть ниже

git submodule update --remote --merge

примечание: последние две команды имеют одинаковое поведение


Я сделал git-клон без подмодулей по ошибке, и все остальные опции не сработали, никто не делал клоны подмодулей. Используя твой, git submodule updateсделал свое дело. Теперь я загружаю данные субмодуля, отсутствующие в первом шаге клона. Спасибо. Я не очень хорош в Git: C
m3nda

Этот ансер на самом деле является очень хорошим ответом, чтобы задать вопрос здесь: почему я должен "... --recursive-submodules ..", а затем дополнительно "... update ..." и даже ".. .foreach ... "позже, чтобы получить последний коммит? Все это не похоже на GIT! Что делает «обновление» и почему я должен вручную переходить на каждый модуль, чтобы вытащить? Разве это не то, что делает "... --recurse-submodules .."? Есть намеки?
Питер Бранфорн

20

Я не знаю, с какой версии git это работает, но вот что вы ищете:

git submodule update --recursive

Я также использую его git pullдля обновления корневого хранилища:

git pull && git submodule update --recursive

10

Приведенные выше ответы хороши, однако мы использовали git-hooks, чтобы сделать это проще, но получается, что в git 2.14 вы можете установитьgit config submodule.recurse значение true, чтобы подмодули обновлялись, когда вы загружаете свой git-репозиторий.

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

Может быть сделано с помощью:

git config submodule.recurse true

Должен любить эту опцию, к сожалению, все еще нужно использовать git submodule initзаранее, хотя, если ваш подмодуль еще не инициализирован.
Пеллет

5

Git для Windows 2.6.3 :

git submodule update --rebase --remote


Это единственный, который работал на меня. Я даже не смог инициировать или обновить, так как указатель субмодуля указывал на версию, которой больше не было в пульте
Павел Р

4

С верхнего уровня в репо:

git submodule foreach git checkout develop
git submodule foreach git pull

Это переключит все ветки на разработку и загрузку последних


2
У меня не работает, с git 2.7.
Бруно Хейбл

У вас есть что-то вроде файла Everything sln, который добавляет все ссылки на проекты в дереве? И какую ошибку вы видите? Можете ли вы проверить файл gitignore тоже
Srayan Guhathakurta

1
git submodule foreach git pull origin masterПришлось добавить ветку, которую я хотел получить. кроме этого, работал отлично.
Торксед

3

Я сделал это, адаптируя gahooa «сек ответ выше :

Интегрировать это с мерзавцем [alias]...

Если ваш родительский проект имеет нечто подобное в .gitmodules:

[submodule "opt/submodules/solarized"]
    path = opt/submodules/solarized
    url = git@github.com:altercation/solarized.git
[submodule "opt/submodules/intellij-colors-solarized"]
    path = opt/submodules/intellij-colors-solarized
    url = git@github.com:jkaving/intellij-colors-solarized.git

Добавьте что-то подобное в ваш .gitconfig

[alias]
    updatesubs = "!sh -c \"git submodule init && git submodule update && git submodule status\" "

Затем, чтобы обновить ваши подмодули, запустите:

git updatesubs

У меня есть пример этого в моем репозитории установки среды .


3

Все, что вам нужно сделать сейчас, это просто git checkout

Просто убедитесь, что он включен через этот глобальный конфиг: git config --global submodule.recurse true


2

Вот командная строка для извлечения из всех ваших репозиториев git, являются ли они подмодулями или нет:

ROOT=$(git rev-parse --show-toplevel 2> /dev/null)
find "$ROOT" -name .git -type d -execdir git pull -v ';'

Если вы запускаете его в своем верхнем git-репозитории, вы можете заменить "$ROOT"на ..


1

Я думаю, вам придется написать сценарий для этого. Если честно, я мог бы установить python для этого, чтобы вы могли использовать os.walkto cdдля каждого каталога и выполнять соответствующие команды. Использование python или другого языка сценариев, отличного от пакетного, позволит вам легко добавлять / удалять подпроекты без необходимости изменять сценарий.


1

Замечание: не слишком легкий способ, но работоспособный и у него есть свои уникальные плюсы.

Если кто-то хочет клонировать только HEADревизию репозитория и только HEADs из всех его подмодулей (т.е. для извлечения «ствола»), тогда можно использовать следующий скрипт Lua . Иногда простая команда git submodule update --init --recursive --remote --no-fetch --depth=1может привести к неисправимой gitошибке. В этом случае необходимо очистить подкаталог .git/modulesкаталога и клонировать подмодуль вручную, используя git clone --separate-git-dirкоманду. Единственная сложность состоит в том, чтобы узнать URL , путь.git каталогу подмодуля и путь к подмодулю в дереве суперпроекта.

Примечание: скрипт проверяется только на наличие https://github.com/boostorg/boost.gitрепозитория. Его особенности: все подмодули размещены на одном хосте и .gitmodulesсодержат только относительные URL-адреса .

-- mkdir boost ; cd boost ; lua ../git-submodules-clone-HEAD.lua https://github.com/boostorg/boost.git .
local module_url = arg[1] or 'https://github.com/boostorg/boost.git'
local module = arg[2] or module_url:match('.+/([_%d%a]+)%.git')
local branch = arg[3] or 'master'
function execute(command)
    print('# ' .. command)
    return os.execute(command)
end
-- execute('rm -rf ' .. module)
if not execute('git clone --single-branch --branch master --depth=1 ' .. module_url .. ' ' .. module) then
    io.stderr:write('can\'t clone repository from ' .. module_url .. ' to ' .. module .. '\n')
    return 1
end
-- cd $module ; git submodule update --init --recursive --remote --no-fetch --depth=1
execute('mkdir -p ' .. module .. '/.git/modules')
assert(io.input(module .. '/.gitmodules'))
local lines = {}
for line in io.lines() do
    table.insert(lines, line)
end
local submodule
local path
local submodule_url
for _, line in ipairs(lines) do
    local submodule_ = line:match('^%[submodule %"([_%d%a]-)%"%]$')
    if submodule_ then
        submodule = submodule_
        path = nil
        submodule_url = nil
    else
        local path_ = line:match('^%s*path = (.+)$')
        if path_ then
            path = path_
        else
            submodule_url = line:match('^%s*url = (.+)$')
        end
        if submodule and path and submodule_url then
            -- execute('rm -rf ' .. path)
            local git_dir = module .. '/.git/modules/' .. path:match('^.-/(.+)$')
            -- execute('rm -rf ' .. git_dir)
            execute('mkdir -p $(dirname "' .. git_dir .. '")')
            if not execute('git clone --depth=1 --single-branch --branch=' .. branch .. ' --separate-git-dir ' .. git_dir .. ' ' .. module_url .. '/' .. submodule_url .. ' ' .. module .. '/' .. path) then
                io.stderr:write('can\'t clone submodule ' .. submodule .. '\n')
                return 1
            end
            path = nil
            submodule_url = nil
        end
    end
end
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.