Як перемістити існуючий підмодуль 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 ".emacs.d/vimpulse"]

або також можливо зберегти лише ім’я підпроекту?

[submodule "vimpulse"]

ПРИМІТКА: ОП відповідає на власне запитання git mvкомандою, прямо в питанні.
Dan Rosenstark

ЗАРАЗ, ви не можете користуватися git mvтаким. Використовуйте deinitтоді, rm як зазначено stackoverflow.com/a/18892438/8047 .
Dan Rosenstark

14
@Yar: принаймні на git 2.0.0, git mv просто працює для підмодулів, нічого іншого не потрібно.
Педро Романо,

9
Починаючи з 1.8.5підмодулів Git переміщення підтримується в основному за допомогою git mvкоманди ( із приміток до випуску , спочатку пов’язаних самим @thisch). Тут також відповіли
dennisschagt

git mvчи переміщують підмодуль у робочій області та оновлюють файли .git підмодулю правильно, але підпапка в папці .git / module батьківського репо залишається такою ж - це гаразд? (Я використовую 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 / модуля до bar / module ви повинні змінити .gitmodules розділ [submodule "foo/module"]на [submodule "bar/module"], а під цим самим розділом - path = foo/moduleна path = bar/module. Крім того , ви повинні змінити в .git / конфігурації розділу [submodule "foo/module"]в [submodule "bar/module"].
wilhelmtell

3
Для мене це не спрацювало ... найближче знайдене нами рішення - видалити підмодуль (біль), а потім повторно додати його в іншому місці.
Пабло Олмос де Агілера C.

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 та каталог підмодулів вже ставляться на коміт (ви можете перевірити це за допомогою git status.)
  4. Вчиніть зміни, git commitі ви готові йти!

Готово!


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 / module / AFNetworking" в ".git / module / ext / AFNetworking"
  3. Перемістіть бібліотеку з "AFNetworking" на "ext / AFNetworking"
  4. Відредагуйте ".git / модулі / 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

Et voila. Наведений вище приклад не змінює глибину каталогів, що суттєво впливає на складність завдання, а також не змінює назву підмодуля (що може насправді не потрібно, але я зробив це, щоб відповідати тому, що станеться, якщо я додати новий модуль на цьому шляху.)


4
Дякую Метт. Я загубився від прийнятої відповіді. Дякую за те, що ви охопили більше ніж базовий випадок Це спрацювало як шарм.
Ендрю Хаббс

Вам не потрібно перетасовувати шляхи .git / module або змінювати назву підмодуля (як згадують Аранд і Боб Белл). Хоча це може зробити речі чистішими.
gatoatigrado

Не забудьте виконати кроки 2, 3, 4 і 5 рекурсивно для будь-яких підмодулів.
herzbube

22

[Оновлення: 2014-11-26] Оскільки Яр добре підсумовує нижче, перш ніж робити що-небудь, переконайтеся, що ви знаєте 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-отримати оновлення sudo apt-get install git
Майкл Коул

@MichaelCole Дякую! Право ти! Див . Примітки до випуску Git-1.8.3 . FYI: 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

Рядок у лапках після "[підмодуля" не має значення. Ви можете змінити його на "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 repos міститься у верхньому сховищі .git dir. Усі катіони з верхнього сховища:

  1. Відредагуйте .gitmodules та змініть налаштування "path =" для відповідного підмодуля. (Не потрібно міняти мітку і не додавати цей файл до індексу.)

  2. Відредагуйте .git / module / name / config та змініть налаштування "worktree =" для відповідного підмодуля

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

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

Цікаво, чи не має значення, якщо сховища є атомними чи відносними підмодулями, у моєму випадку це було відносно (підмодуль / .git - це повернення до topproject / .git / module / submodule)


2

Просто використовуйте скрипт оболонки git-submodule-move .


Гей, я знову підняв це запитання і використав одну з вищих проголосованих відповідей, і тепер мені б хотілося, щоб я прокрутився вниз і побачив свою попередню відповідь, про яку я забув.
Флейм

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
Гарне резюме. git mvХоча +1 має бути кращим в останніх версіях Git.
VonC

@VonC Я тестував на git 1.8.5, впевнений, що це настільки ж добре, як і для нього mv. Дякую!
Дан Розенстарк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.