Как переместить существующий подмодуль Git в репозиторий Git?

356

Я хотел бы изменить имя каталога подмодуля Git в моем суперпроекте Git.

Предположим, у меня есть следующая запись в моем .gitmodulesфайле:

[submodule ".emacs.d/vimpulse"]  
path = .emacs.d/vimpulse  
url = git://gitorious.org/vimpulse/vimpulse.git

Что мне нужно ввести, чтобы переместить .emacs.d/vimpulseкаталог, .emacs.d/vendor/vimpulseне удаляя его сначала (объяснено здесь и здесь ), а затем повторно добавить его.

Нужен ли Git весь путь в теге submodule?

[submodule ".emacs.d/vimpulse"]

или возможно также сохранить только название подпроекта?

[submodule "vimpulse"]
thisch
источник
ПРИМЕЧАНИЕ: ОП отвечает на свой вопрос git mvкомандой, прямо в вопросе.
Дэн Розенстарк
ОДНАКО, вы не можете использовать git mvкак это. Используйте deinitтогда rm как указано stackoverflow.com/a/18892438/8047 .
Дэн Розенстарк
14
@Yar: по крайней мере на git 2.0.0, git mv просто работает и для подмодулей, больше ничего не нужно.
Педро Романо
9
Начиная с Git, 1.8.5перемещение субмодулей поддерживается изначально с помощью git mvкоманды ( из заметок о выпуске , сначала связанных с самим @thisch). Также здесь ответили
dennisschagt
git mvперемещает подмодуль в рабочей области и корректно обновляет файлы .git подмодуля, но подпапка в папке .git / modules родительского репозитория остается прежней - это нормально? (Я использую Git 2.19.0 на Windows)
Yoyo

Ответы:

377

Примечание: как упоминалось в комментариях, этот ответ относится к шагам, необходимым для более старых версий git. Git теперь имеет встроенную поддержку для перемещения подмодулей:

Начиная с git 1.8.5, git mv old/submod new/submodработает как положено и выполняет всю сантехнику за вас. Возможно, вы захотите использовать git 1.9.3 или новее, потому что он содержит исправления для перемещения подмодуля.


Процесс аналогичен удалению подмодуля (см. Как удалить подмодуль? ):

  1. Отредактируйте .gitmodulesи измените путь подмодуля соответствующим образом и поместите его в индекс с помощью git add .gitmodules.
  2. При необходимости создайте родительский каталог для нового местоположения субмодуля ( mkdir -p new/parent).
  3. Переместить весь контент из старого в новый каталог ( mv -vi old/parent/submodule new/parent/submodule).
  4. Убедитесь, что Git отслеживает этот каталог ( git add new/parent).
  5. Удалите старый каталог с помощью git rm --cached old/parent/submodule.
  6. Переместить каталог .git/modules/old/parent/submoduleсо всем его содержимым в .git/modules/new/parent/submodule.
  7. Отредактируйте .git/modules/new/parent/configфайл, убедитесь, что элемент рабочего дерева указывает на новые местоположения, так что в этом примере это должно быть worktree = ../../../../../new/parent/module. Обычно ..в прямом пути в этом месте должно быть больше двух каталогов.
  8. Отредактируйте файл new/parent/module/.git, убедитесь, что путь в нем указывает на правильное новое местоположение в основной .gitпапке проекта , поэтому в этом примере gitdir: ../../../.git/modules/new/parent/submodule.

    git status вывод выглядит так для меня потом:

    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       modified:   .gitmodules
    #       renamed:    old/parent/submodule -> new/parent/submodule
    #
    
  9. Наконец, внесите изменения.

Аксель Бекерт
источник
37
При обновлении .gitmodules убедитесь, что вы обновили и эту pathконфигурацию, и имя подмодуля. Например, при перемещении Foo / модуль бар / модуль необходимо изменить в .gitmodules раздел [submodule "foo/module"]к [submodule "bar/module"], и при этом же разделе path = foo/moduleв path = bar/module. Кроме того, вы должны изменить в .git / config раздел [submodule "foo/module"]на [submodule "bar/module"].
Вильгельмтелл
3
У меня это тоже не сработало ... самое близкое решение, которое я нашел, это удалить подмодуль (боль), а затем повторно добавить его в другое место.
Пабло Олмос де Агилера С.
33
Очень-очень важное замечание: если вы fatal: 'git status --porcelain' failed in...просто удалите файлы .git или каталоги в подмодуле.
антитоксический
19
Похоже, что в этом сообщении пропущено несколько шагов, таких как редактирование .git/modules/old/parent/submodule, перемещение его на новое место, обновление gitdirв old/parent/submodule/.git...
szx
38
Начиная с git 1.8.5, git mv old/submod new/submodработает как положено и выполняет всю сантехнику за вас. Возможно, вы захотите использовать git 1.9.3+, потому что он включает в себя исправления для перемещения подмодулей.
Валлорик
232

Самый современный ответ, взятый из комментария Валлорика выше:

  1. Обновление до Git 1.9.3 (или 2.18, если подмодуль содержит вложенные подмодули )
  2. git mv old/submod new/submod
  3. После этого .gitmodules и каталог submodule уже подготовлены для фиксации (вы можете проверить это с помощью git status.)
  4. Передайте изменения с, git commitи вы готовы!

Выполнено!

phatmann
источник
3
Это действительно работает, 1.9.3 за исключением подмодуля внутри перемещенного подмодуля. Это потребовало некоторой ручной очистки.
Паскаль
3
Это должно уже работать в версии, 1.8.5как описано в примечаниях к выпуску .
dennisschagt
6
Этот ответ должен получить 1000 голосов, я почти испортил репо, выполнив шаги, описанные выше, на самом деле в StackOverflow должен быть сценарий использования для этой ситуации.
MGP
5
Вау, это сработало как шарм (git 1.9.5), хотелось бы, чтобы это был выбранный ответ.
Алексей Ильяев
7
Одна вещь, которую это не делает, - то, что это не изменяет начальную метку для подмодуля. Если вы проверите .gitmodulesфайл, old/submodон все еще будет использоваться в качестве метки для субмодуля, пока путь был изменен. Чтобы изменить метку, кажется, вам нужно переместить путь к каталогу модулей внутри .git, а затем вручную изменить метку .gitmodules.
CMCDragonkai
55

В моем случае я хотел переместить подмодуль из одного каталога в подкаталог, например, «AFNetworking» -> «ext / AFNetworking». Вот шаги, которые я выполнил:

  1. Отредактируйте .gitmodules, изменив имя подмодуля и путь к «ext / AFNetworking»
  2. Переместите каталог git подмодуля из ".git / modules / AFNetworking" в ".git / modules / ext / AFNetworking"
  3. Переместить библиотеку из "AFNetworking" в "ext / AFNetworking"
  4. Отредактируйте «.git / modules / ext / AFNetworking / config» и исправьте [core] worktreeстроку. Мой изменен с ../../../AFNetworkingна../../../../ext/AFNetworking
  5. Отредактируйте "ext / AFNetworking / .git" и исправьте gitdir. Мой изменен с ../.git/modules/AFNetworkingна../../git/modules/ext/AFNetworking
  6. git add .gitmodules
  7. git rm --cached AFNetworking
  8. git submodule add -f <url> ext/AFNetworking

Наконец, я увидел в Git статус:

matt$ git status
# On branch ios-master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   .gitmodules
#   renamed:    AFNetworking -> ext/AFNetworking

И вуаля. Приведенный выше пример не меняет глубину каталога, что сильно влияет на сложность задачи, и не меняет имя подмодуля (что может и не быть необходимым, но я сделал это, чтобы соответствовать тому, что произойдет, если я добавлю новый модуль по этому пути.)

Мэтт Коннолли
источник
4
Спасибо Мэтт. Я был потерян на принятом ответе. Спасибо за освещение больше, чем базовый случай. Это работает как шарм.
Эндрю Хаббс
Вам не нужно перебирать пути .git / modules или менять имя подмодуля (как упоминают arand и Bob Bell). Тем не менее, делая это может держать вещи чище.
gatoatigrado
Не забудьте сделать шаги 2, 3, 4 и 5 рекурсивно для любых субмодулей.
herzbube
22

[Обновление: 2014-11-26] Поскольку Yar подводит итоги ниже, прежде чем что-либо делать, убедитесь, что вы знаете URL-адрес субмодуля. Если неизвестно, откройте .git/.gitmodulesи осмотрите ключ submodule.<name>.url.

То, что работало для меня, было удалить старый подмодуль, используя git submodule deinit <submodule>затем git rm <submodule-folder>. Затем снова добавьте подмодуль с новым именем папки и подтвердите. Проверка состояния git перед фиксацией показывает старый подмодуль, переименованный в новое имя и модифицированный .gitmodule.

$ git submodule deinit foo
$ git rm foo
$ git submodule add https://bar.com/foo.git new-foo
$ git status
renamed:    foo -> new-foo
modified:   .gitmodules
$ git commit -am "rename foo submodule to new-foo"
Марк Микофски
источник
1
Для этого требуется git 1.8.3 или выше. Смотрите этот пост для обновления вашего git: evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntu
Майкл Коул
1
Или лучший способ: sudo add-apt-repository ppa: git-core / ppa sudo apt-get update sudo apt-get install git
Майкл Коул
@MichaelCole Спасибо! Вы правы! Смотрите Git-1.8.3 Замечания к выпуску . К вашему сведению: Ubuntu-13.10 (Saucy Salamander) имеет Git-1.8.3.2 , но приятно знать, что есть ppa . Кроме того, IMHO стратегия слияния git subtree - лучший подход; Я отказался от подмодулей для своих собственных проектов. Все еще хорошо понять для существующих проектов.
Марк Микофски
Я пробовал несколько решений, но ваше лучшее. Используйте только командную строку, чтобы вам не нужно (и не нужно) изменять какие-либо git-файлы. Спасибо!
nahung89
12

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

  • Переместите субмодуль в его новый дом.
  • Отредактируйте .gitфайл в рабочем каталоге подмодуля и измените путь, который он содержит, чтобы он указывал на правильный каталог в каталоге главного репозитория .git/modules.
  • Войдите в .git/modulesкаталог главного репозитория и найдите каталог, соответствующий вашему подмодулю.
  • Отредактируйте configфайл, обновив worktreeпуть таким образом, чтобы он указывал на новое местоположение рабочего каталога подмодуля.
  • Отредактируйте .gitmodulesфайл в корне главного репозитория, обновив путь к рабочему каталогу подмодуля.
  • git add -u
  • git add <parent-of-new-submodule-directory>(Важно добавить родителя , а не сам каталог подмодулей.)

Несколько заметок:

  • Эти [submodule "submodule-name"]строки в .gitmodulesи .git/configдолжны соответствовать друг другу, но не соответствуют ни к чему другому.
  • Подмодуль рабочего каталога и .gitкаталога должен правильно указывать друг на друга.
  • .gitmodulesИ .git/configфайлы должны быть синхронизированы.
Пол Гидеон Данн
источник
9

Строка в кавычках после "[submodule" не имеет значения. Вы можете изменить его на «foobar», если хотите. Он используется для поиска подходящей записи в ".git / config".

Поэтому, если вы сделаете изменение до запуска «git submodule init», оно будет работать нормально. Если вы внесете изменение (или получите его с помощью слияния), вам нужно будет либо вручную отредактировать .git / config, либо снова запустить «git submodule init». Если вы сделаете последнее, у вас останется безобидная «застрявшая» запись со старым именем в .git / config.

Боб Белл
источник
Это действительно раздражает, но ты прав. Хуже всего то, что если вы просто измените URL, запуск git init, похоже, не обновит его, вам придется отредактировать .git / config вручную.
crimson_penguin
1
в этом случае git submodule syncизменения распространяются .git/configавтоматически
CharlesB
9

Вы можете просто добавить новый подмодуль и удалить старый подмодуль, используя стандартные команды. (должен предотвращать любые случайные ошибки внутри .git)

Пример настройки:

mkdir foo; cd foo; git init; 
echo "readme" > README.md; git add README.md; git commit -m "First"
## add submodule
git submodule add git://github.com/jquery/jquery.git
git commit -m "Added jquery"
## </setup example>

Пример перемещения 'jquery' в 'vendor / jquery / jquery':

oldPath="jquery"
newPath="vendor/jquery/jquery"
orginUrl=`git config --local --get submodule.${oldPath}.url`

## add new submodule
mkdir -p `dirname "${newPath}"`
git submodule add -- "${orginUrl}" "${newPath}"

## remove old submodule
git config -f .git/config --remove-section "submodule.${oldPath}"
git config -f .gitmodules --remove-section "submodule.${oldPath}"
git rm --cached "${oldPath}"
rm -rf "${oldPath}"              ## remove old src
rm -rf ".git/modules/${oldPath}" ## cleanup gitdir (housekeeping)

## commit
git add .gitmodules
git commit -m "Renamed ${oldPath} to ${newPath}"

Бонусный метод для больших подмодулей:

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

Пример (используйте тот же пример настройки)

oldPath="jquery"
newPath="vendor/jquery/jquery"
baseDir=`pwd`
orginUrl=`git config --local --get submodule.${oldPath}.url`

# add new submodule using old submodule as origin
mkdir -p `dirname "${newPath}"`
git submodule add -- "file://${baseDir}/${oldPath}" "${newPath}"

## change origin back to original
git config -f .gitmodules submodule."${newPath}".url "${orginUrl}"
git submodule sync -- "${newPath}"

## remove old submodule
...
Ланс Рашинг
источник
Если вы не используете head, вам также может понадобиться проверить правильную версию модуля по адресу newPath.
paulmelnikow
2

Данное решение не сработало для меня, однако аналогичная версия сработала ...

Это с клонированным репозиторием, поэтому подмодульные репозитории git содержатся в верхних репозиториях .git dir. Все катионы находятся в верхнем хранилище:

  1. Отредактируйте .gitmodules и измените настройку «путь =» для рассматриваемого подмодуля. (Нет необходимости ни менять метку, ни добавлять этот файл в индекс.)

  2. Отредактируйте .git / modules / name / config и измените настройку «worktree =» для рассматриваемого подмодуля

  3. запустить:

    mv submodule newpath/submodule
    git add -u
    git add newpath/submodule
    

Интересно, имеет ли это значение, если репозитории являются атомарными или относительными подмодулями, в моем случае это было относительно (submodule / .git является ссылкой на topproject / .git / modules / submodule)

Arand
источник
2

Просто используйте скрипт оболочки git-submodule-move .

Флимм
источник
Хех, я снова посмотрел на этот вопрос и использовал один из ответов с более высоким рейтингом, и теперь я хотел бы прокрутить вниз и увидеть свой предыдущий ответ, о котором я забыл.
Flimm
2

Я только что прошел это испытание вчера, и этот ответ сработал отлично. Вот мои шаги, для ясности:

  1. Убедитесь, что субмодуль зарегистрирован и отправлен на его сервер. Вы также должны знать, на какой ветке это происходит.
  2. Вам нужен URL вашего субмодуля! использованиеmore .gitmodules потому что, как только вы удалите подмодуль, он не будет вокруг
  3. Теперь вы можете использовать deinit, rmа затемsubmodule add

ПРИМЕР

КОМАНДЫ

    git submodule deinit Classes/lib/mustIReally
    git rm foo
    git submodule add http://developer.audiob.us/download/SDK.git lib/AudioBus

    # do your normal commit and push
    git commit -a 

ПРИМЕЧАНИЕ: git mv не делает этого. Совсем.

Дэн Розенстарк
источник
3
Хорошее резюме. +1 git mvдолжно быть лучше в самых последних версиях Git.
VonC
@VonC Я тестировал на git 1.8.5, почти уверен, что это так же хорошо, как и для mv. Спасибо!
Дэн Розенстарк