Оновіть підмодуль Git до останньої передачі даних про походження


853

У мене є проект з підмодулем 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-навчав модуль і повторно додавав його, але це в принципі неправильно і набридає на практиці.


5
Відповідь Девіда З виглядає як кращий спосіб зробити це - тепер, коли Git має функціонал, який вам потрібно вбудовувати через --remoteопцію, можливо, було б корисно відзначити це як прийняту відповідь, а не підхід "від руки" у відповіді Джейсона?
Марк Амері

1
Я дуже згоден з @MarkAmery. Хоча Джейсон давав робоче рішення, це не призначений спосіб зробити це, оскільки він залишає покажчик фіксації підмодуля на неправильному ідентифікаторі фіксації. Нове --remote, безумовно, є кращим рішенням на даний момент часу, і оскільки це питання пов'язане з Github Gist щодо підмодулів, я вважаю, що краще читачам, що надходять, побачити нову відповідь.
MutantOctopus

Приємний дотик із hunter2паролем: o)
lfarroco

Відповіді:


1458

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

335
git submodule foreach git pull
Mathias Bynens

87
@Nicklas У цьому випадку використовуйте git submodule foreach git pull origin master.
Mathias Bynens

54
На цьому етапі, з усіма цими виправленнями виправлень, мені потрібно, щоб хтось написав пояснювальну публікацію в блозі і вказав мені туди. Будь ласка.
Suz

25
незначне вдосконалення підходу "foreach" - ви можете додати там - рекурсивний, якщо у вас є підмодулі в межах підмодулів. так: git submodule foreach --recursive git pull origin master.
orion elenzil

4
@Abdull -aПеремикач команди git commit"Скажіть [s] команді автоматично встановлювати файли, які були змінені та видалені, але нові файли, про які Git не повідомив, не впливають."
godfrzero

473

У Git 1.8.2 є нова опція --remote, яка дозволить саме таку поведінку. Біг

git submodule update --remote --merge

отримає останні зміни з висхідного потоку в кожному підмодулі, об'єднає їх і перевірить останню редакцію підмодуля. Як зазначено в документації :

--ремонт

Ця опція діє лише для команди оновлення. Замість використання записаного SHA-1 надпроекту для оновлення підмодулю використовуйте стан відділення дистанційного відстеження підмодуля.

Це еквівалентно запуску git pullв кожному підмодулі, що, як правило, саме те, що ви хочете.


4
"еквівалент запуску git pullв кожному підмодулі" Для уточнення, різниця (з точки зору користувача) між вашою відповіддю та git submodule foreach git pull?
Денніс

3
@Dennis це по суті те саме, але я не впевнений, чи функціональність точно така ж. Можливо, є деякі незначні відмінності, про які я не знаю, наприклад, у тому, як обидві команди реагують на деякі налаштування конфігурації.
David Z

5
Я б хотів, щоб я міг повернути ці 10 000X. Чому це ніде не відображено в документації git? Величезний нагляд.
serraosays

4
Для мене вони насправді значно відрізнялися; foreach git pullлише перевірив їх, але не оновив вказівник основного репо, щоб вказати на новіший комітет підмодуля. Тільки з --remoteцим це вказувало на останню прихильність.
Ela782

5
чому опція --merge? Яка різниця?
mFeinstein

127

У своєму батьківському каталозі проекту запустіть:

git submodule update --init

Або якщо у вас запущені рекурсивні підмодулі:

git submodule update --init --recursive

Іноді це все ще не працює, тому що якимось чином ви маєте місцеві зміни в локальному каталозі підмодулів під час оновлення підмодуля.

Більшість випадків місцеві зміни можуть бути не тими, які ви хочете зробити. Це може статися через видалення файлу у вашому підмодулі тощо. Якщо це так, зробіть скидання у вашому локальному каталозі підмодуля та у своєму батьківському каталозі проекту, запустіть ще раз:

git submodule update --init --recursive

5
це правдива відповідь. чи можу я якось натиснути його до мого віддаленого сховища?
MonsterMMORPG

Це працює для нових підмодулів! Я міг би оновити всі інші, але папка нових підмодулів залишатиметься порожньою, поки я не запустив цю команду.
Алексіс Вільке

1
Це не тягне змін до існуючих підмодулів
Сергій Г.

73

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


24

Схоже, два різних сценарії змішуються в цій дискусії:

Сценарій 1

Використовуючи вказівники мого батьківського репозиторію на підмодулі, я хочу перевірити фіксацію у кожному підмодулі, на який вказує батьківське сховище, можливо після першої ітерації через усі підмодулі та їх оновлення / витягування з віддаленого.

Це, як вказувалося, робиться з

git submodule foreach git pull origin BRANCH
git submodule update

Сценарій 2, на який, на мою думку, спрямований ОП

У одному або декількох підмодулях трапляються нові речі, і я хочу 1) здійснити ці зміни та 2) оновити батьківський сховище, щоб вказати на головну (останню) фіксацію цього / цих підмодулів.

Це було б зроблено до

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), щоб вказати на голову підмодуля (ів).

Для цього я створив цей маленький сценарій Баша:

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- це те, щоб сценарій продовжувався на помилках. Щоб зробити цю роботу, все в ітерації повинно бути загорнене в подвійні лапки, а команди 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для оновлення батьківського сховища.

Нарешті, команда з деяким повідомленням, що пояснює, що батьківське сховище оновлено. Ця фіксація буде ігнорована за замовчуванням, якщо нічого не було зроблено. Надішліть це на початок, і ви закінчите.

У мене є сценарій, який виконує цю роботу в роботі Дженкінса, яка приєднується до запланованого автоматизованого розгортання після цього, і це працює як шарм.

Я сподіваюся, що це комусь допоможе.


2
! @ # $% SO Ми використовуємо сценарії, подібні до ваших; одна примітка: Замість `` git submodule foreach --quiet 'echo $ path' '`` ми використовуємо `` git submodule foreach --recursive --quiet pwd `` `всередині для циклів. pwdКоманда друкує правильний «абсолютний шлях» для кожного субмодуля сьогодення; --recursiveгарантує, що ми відвідуємо всі підмодулі, включаючи підмодулі в межах підмодулів ..., які можуть бути присутніми у великому проекті. Обидва методи спричиняють проблеми з каталогами, що включають пробіли, наприклад, /c/Users/Ger/Project\ Files/...політика полягає у тому, щоб ніколи не використовувати пробіли ніде в наших проектах.
Гер Хоббельт

2
Це добре, і ви маєте рацію, що в деяких відповідях щодо того, що таке питання, є непорозуміння, але, як вказує відмінна відповідь Девіда Z, ваш сценарій непотрібний, оскільки функціональність була вбудована в Git з середини 2013 року, коли вони додали --remoteваріант. git submodule update --remoteповодиться приблизно так, як робить ваш сценарій.
Марк Амері

@GerHobbelt Дякую Ви маєте рацію, у нас є лише 1 рівень підмодулів, тому я ніколи не думав зробити це рекурсивним. Я не оновлюю скрипт, перш ніж у мене з'явилася можливість переконатися, що він працює, як очікувалося, але, безумовно, мій сценарій буде підтримувати підмодулі. Щодо пробілів у папках, це безумовно звучить як щось, чого слід уникати! : S
Frederik Struck-Schøning

@MarkAmery Дякуємо за ваш відгук. Я бачу, однак, 1 випуск: не за аргументом неможливо вказати гілку для підмодулів. З посібника з git: The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).я не хочу редагувати .gitmodules ні .git / config кожен раз, коли я хочу це зробити для іншої гілки, ніж для master. Але, може, я щось пропустив? Також метод, здається, примушує рекурсивні злиття (таким чином, відсутня можливість швидкого перемотування вперед).
Фредерік Струк-Шонінг

Останнє: я спробував метод @ DavidZ, і він, схоже, не робив саме того, я вирішив зробити (і про те, що про це просили): Додавання команд HEAD підмодулів до батьківського (тобто "оновлення покажчика" ). Однак, схоже, це єдина робота дуже добре (і швидше) з отриманням та об'єднанням останніх змін у всіх підмодулях. На жаль, за замовчуванням лише з головного відділення (якщо ви не редагуєте файл .gitmodules (див. Вище)).
Фредерік Струк-Шонінг

19

Простий і простий, щоб отримати підмодулі:

git submodule update --init --recursive

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

git submodule foreach git pull origin master

12

Зауважте, хоча сучасна форма оновлення підмодулю буде такою:

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

Старша форма була:

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

За винятком ... ця друга форма насправді не "тиха".

Див. Комісію a282f5a (12 квітня 2019 р.) Від Nguyễn Thái Ngọc Duy ( 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як варіанти, навіть якщо вони виглядають як один.


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

Див. Комісію 30db18b (24 червня 2019 р.) Від Моріана Сонета ( momoson) .
(Об’єднано Хуніо С Хамано - gitster- у комітеті 968eecb , 09 липня 2019 р.)

submodule foreach: виправити рекурсію варіантів

Дзвінки:

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

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


7
git pull --recurse-submodules

Це призведе до всіх останніх зобов'язань.


4

У своєму випадку я хотів gitоновити до останнього і одночасно повторно заповнити будь-які відсутні файли.

Наступне відновило відсутні файли (завдяки --forceяким, здається, тут не згадувалося), але це не спричинило жодних нових комітетів:

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

Це зробило:

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


3

@Jason правильний певним чином, але не повністю.

оновлення

Оновіть зареєстровані підмодулі, тобто клонуйте відсутні модулі та перевірте комісію, вказану в індексі, що містить сховище. Це зробить підмодулі HEAD від'єднаними, якщо не вказано --rebase або --merge або ключовий підмодуль.

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


1
Здається, що якщо я переміщу підмодуль на інший фіксатор, а потім запускаю git submodule update, оновлення перемістить підмодуль до комітки, визначеної в поточній HEAD надпроекту. (як би не було останнього вчинення в суперпроекті, підпроект повинен бути - така поведінка, після пояснення в публікації Джейсона, мені здається логічною) Це також здається, що це відбувається, але лише у випадку, якщо підпроект знаходиться на неправильній комісії , що додало моєї плутанини.
Танатос

2

Ось дивовижний однокласник, щоб оновити все до останнього на майстер:

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

Завдяки Марку Жакіту


2

Якщо ви не знаєте відділення хоста, зробіть це:

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

Він отримає гілку основного сховища Git, а потім для кожного підмодуля зробить витяг з тієї ж гілки.


0

Якщо ви хочете оформити masterвідділення для оформлення кожного підмодуля - для цього можна скористатись такою командою:

git submodule foreach git checkout master
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.