На користь читача, це тут намагається підсумувати його та дати покрокове керівництво щодо того, як це зробити, якщо все не працює так, як очікувалося. Далі йде перевірений і безпечний спосіб для git
версії 2.17
та вище для позбавлення від підмодуля :
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
- Якщо це не допоможе вам, дивіться нижче.
- Немає варіантів. Нічого небезпечного. І навіть не думайте робити більше!
- Тестовано з Debian Buster
2.20.1
та Ubuntu 18.04 2.17.1
.
"$submodule"
це лише підкреслити, де слід вказати ім’я, і що вам слід бути обережними з пробілами тощо
- Якщо в Windows ігноруйте перший рядок і замініть
"$submodule"
Windows способом правильно вказаного шляху до підмодуля. (Я не Windows)
Увага!
Ніколи не торкайтеся нутрощів .git
каталогу самостійно! Редагування всередині.git
переходить у темну сторону. Тримайтеся подалі за будь-яку ціну!
І так, ви можете звинуватити git
в цьому, оскільки багато корисних речей не вистачалоgit
минулому. Як правильний спосіб знову видалити підмодулі.
Я думаю, що в документації Росії є дуже небезпечна частина git submodule
. Рекомендує зняти $GIT_DIR/modules/<name>/
себе.
На моє розуміння, це не просто невірно, це надзвичайно небезпечно і провокує великі головні болі в майбутньому! Дивись нижче.
Зауважте, що
git module deinit
є прямою оберненою до
git module init
але
git submodule deinit -- module
git rm -- module
також є досить зворотним
git submodule add -- URL module
git submodule update --init --recursive -- module
тому що деяким командам в основному потрібно зробити більше, ніж просто одну річ:
git submodule deinit -- module
- (1) оновлення
.git/config
git rm
- (2) видаляє файли модуля
- (3) тим самим рекурсивно видаляє підмодулі підмодуля
- (4) оновлення
.gitmodules
git submodule add
- тягне дані до
.git/modules/NAME/
- (1) робить
git submodule init
, тому оновлення.git/config
- (2) робить
git submodule update
, таким чином, нерекурсивно перевіряє модуль
- (4) оновлення
.gitmodules
git submodule update --init --recursive -- module
- за потреби подає додаткові дані
- (3) рекурсивно перевіряє підмодулі підмодуля
Це не може бути повністю симетричним, оскільки зберігати його строго симетрично не має особливого сенсу. Просто не потрібно більше двох команд. Також "втягування даних" є неявним, оскільки вам це потрібно, але видалення кешованої інформації не робиться, оскільки це зовсім не потрібно і може стерти дорогоцінні дані.
Це справді дивовижне для новачків, але в основному це добре: git
просто робить очевидно і робить це правильно, і навіть не намагається робити більше. git
це інструмент, який повинен зробити надійну роботу, а не бути лише черговим "Eierlegende Wollmilchsau" ("Eierlegende Wollmilchsau" перекладається для мене "якоюсь злою версією ножа швейцарської армії").
Тож я розумію скарги людей, кажучи: "Чому я не робиш git
очевидного для мене". Це тому, що "очевидне" тут залежить від точки зору. Надійність у кожній ситуації набагато важливіша. Отже, те, що для вас очевидно, часто не є правильним у всіх можливих технічних ситуаціях. Пам'ятайте про це: AFAICS git
слідує технічному шляху, а не соціальному. (Звідси розумна назва: git)
Якщо це не вдасться
Команди, наведені вище, можуть не працювати через:
- Ваш
git
занадто старий. Потім використовуйте новіший git
. (Дивіться нижче, як це зробити.)
- Ви не передали дані і можете втратити дані. Тоді краще спочатку скопіюйте їх.
- Ваш підмодуль у певному
git clean
сенсі не чистий . Потім спочатку очистіть підмодуль за допомогою цієї команди. (Дивись нижче.)
- Ви раніше робили щось, що не підтримується
git
. Тоді ви на темній стороні, і справи стають потворними і складними. (Можливо, використання іншої машини виправляє це.)
- Можливо, є більше способів невдачі, про які я не знаю (я просто якийсь
git
владний користувач.)
Наступні можливі виправлення.
Використовуйте новіший git
Якщо Ваш апарат занадто старий , немає submodule deinit
в вашому git
. Якщо ви не хочете (або можете) оновити свою git
, тоді просто використовуйте іншу машину з новішою git
! git
призначений для повного розповсюдження, тому ви можете використовувати іншу, git
щоб виконати роботу:
workhorse:~/path/to/worktree$ git status --porcelain
не повинен нічого виводити! Якщо це так, спочатку почистіть речі!
workhorse:~/path/to/worktree$ ssh account@othermachine
othermachine:~$ git clone --recursive me@workhorse path/to/worktree/.git TMPWORK && cd TMPWORK
- Тепер робимо підмодуль
othermachine:~/TMPWORK$ git commit . -m . && exit
workhorse:~/path/to/worktree$ git fetch account@othermachine:TMPWORK/.git
workhorse:~/path/to/worktree$ git merge --ff-only FETCH_HEAD
. Якщо це не працює, використовуйтеgit reset --soft FETCH_HEAD
- Тепер прибирайте речі, поки
git status
знову не буде чисто. Ви можете це зробити, тому що ви очистили це раніше, завдяки першому кроку.
Це othermachine
може бути якийсь VM або якийсь Ubuntu WSL під Windows. Навіть chroot
(але я припускаю, що ви не кореневий, тому що, якщо ви є, root
його слід легше оновити на новіші git
).
Зауважте, що якщо ви не можете ssh
ввійти, існує набір шляхів транспортування git
сховищ. Ви можете скопіювати своє робоче дерево на якийсь USB-накопичувач (включаючи .git
каталог) та клонувати його з флешки. Клонуйте копію, просто щоб знову зробити речі в чистому вигляді. Це може бути PITA, якщо ваші підмодулі не доступні безпосередньо з інших машин. Але і для цього є рішення:
git config --add url.NEWURLPREFIX.insteadOf ORIGINALURLPREFIX
Ви можете використовувати це множення, і це зберігається в $HOME/.gitconfig
. Щось на зразок
git config --add 'url./mnt/usb/repo/.insteadof' https://github.com/
переписує такі URL-адреси, як
https://github.com/XXX/YYY.git
в
/mnt/usb/repo/XXX/YYY.git
Це легко, якщо ви почнете звикати до таких потужних git
функцій.
Спочатку почистіть речі
Чистка вручну - це добре, тому що ви, можливо, виявляєте деякі речі, про які ви забули.
- Якщо git поскаржиться на незбережені речі, зробіть їх і кудись надійніше.
- Якщо git скаржиться на деякі залишки,
git status
іgit clean -ixfd
це ваш друг
- Постарайтеся утриматися від варіантів
rm
і до deinit
тих пір, поки зможете. Параметри (як -f
) для git
хороших, якщо ви професіонал. Але, як ви приїхали сюди, ви, мабуть, не так досвідчені в цьому submodule
районі. Тож краще бути в безпеці, ніж шкодувати.
Приклад:
$ git status --porcelain
M two
$ git submodule deinit two
error: the following file has local modifications:
two
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'two' contains local modifications; use '-f' to discard them
$ cd two
$ git submodule deinit --all
error: the following file has local modifications:
md5chk
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'md5chk' contains local modifications; use '-f' to discard them
$ cd md5chk
$ git submodule deinit --all
error: the following file has local modifications:
tino
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'tino' contains local modifications; use '-f' to discard them
$ cd tino
$ git status --porcelain
?? NEW
$ git clean -i -f -d
Would remove the following item:
NEW
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers 4: ask each
5: quit 6: help
What now> 1
Removing NEW
$ cd ../../..
$ git status --porcelain
$ git submodule deinit two
Cleared directory 'two'
Submodule 'someunusedname' (https://github.com/hilbix/src.git) unregistered for path 'two'
Розумієте, немає -f
необхідності на submodule deinit
. Якщо речі чисті, в певному git clean
сенсі. Також зауважте, що git clean -x
це не потрібно. Це означає, що git submodule deinit
безумовно видаляються незатребувані файли, які ігноруються. Зазвичай це те, що ви хочете, але не забувайте про це. Іноді ігноровані файли можуть бути дорогоцінними, як кешовані дані, на які потрібно заново обчислювати години.
Чому ніколи не видаляють $GIT_DIR/modules/<name>/
?
Можливо, люди хочуть видалити кешований сховище, оскільки вони бояться зіткнутися з проблемою пізніше. Це правда, але наткнутися на цю "проблему" - це правильний спосіб її вирішити! Оскільки виправити це легко, і правильно зробити, ви зможете жити щасливо до цього часу. Це дозволяє уникнути більш громіздких проблем, ніж при видаленні даних самостійно.
Приклад:
mkdir tmptest &&
cd tmptest &&
git init &&
git submodule add https://github.com/hilbix/empty.git two &&
git commit -m . &&
git submodule deinit two &&
git rm two &&
git commit -m . &&
git submodule add https://github.com/hilbix/src.git two
Останній рядок видає наступну помилку:
A git directory for 'two' is found locally with remote(s):
origin https://github.com/hilbix/empty.git
If you want to reuse this local git directory instead of cloning again from
https://github.com/hilbix/src.git
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
Чому ця помилка? Оскільки .git/modules/two/
раніше було заповнено з https://github.com/hilbix/empty.git, а тепер буде повторно заселене чимось іншим, а саме https://github.com/hilbix/src.git . Ви не побачите цього, якщо повторно заселите його з https://github.com/hilbix/empty.git
Що ж тепер робити? Ну просто роби точно так, як сказано! Використовуйте--name someunusedname
git submodule add --name someunusedname https://github.com/hilbix/src.git two
.gitmodules
то схожий
[submodule "someunusedname"]
path = two
url = https://github.com/hilbix/src.git
ls -1p .git/modules/
дає
someunusedname/
two/
Таким чином, у майбутньому ви можете перемикати гілки / здійснювати вперед та назад і більше ніколи не потраплятимете в будь-які проблеми через two/
наявність двох різних (і, можливо, несумісних) сховищ вище. А найкраще: Ви обидва зберігаєте кешований локально.
- Це стосується не тільки вас. Це також справедливо для всіх інших, що використовують ваше сховище.
- І ви не втрачаєте історію. Якщо ви забули натиснути саму останню версію старого підмодуля, ви можете ввести локальну копію і зробити це пізніше. Зауважте, що досить часто хтось забуває натиснути на якісь підмодулі (адже це ПДФА для новачків, поки вони не звикли
git
).
Однак якщо ви видалили кешований каталог, обидві різні каси натикатимуться один на одного, тому що ви не будете використовувати --name
варіанти, правда? Тому щоразу, коли ви здійснюєте оформлення замовлення, вам, можливо, доведеться видаляти .git/modules/<module>/
каталог знову і знову. Це надзвичайно громіздко і ускладнює використання чогось подібного git bisect
.
Тому є дуже технічна причина зберегти цей каталог модулів як заповнювач. Люди, які рекомендують видалити щось нижче, .git/modules/
або не знають краще, або забувають сказати вам, що це робить такі потужні функції, якgit bisect
майже неможливо використовувати, якщо це перетинає таку несумісність підмодуля.
Наступна причина показана вище. Подивись наls
. Що ти там бачиш?
Ну, 2-й варіант модуля two/
не під .git/modules/two/
, він під .git/modules/someunusedname/
! Тож такі речі git rm $module; rm -f .git/module/$module
абсолютно неправильні! Ви повинні або проконсультуватися, module/.git
або.gitmodules
знайти потрібну річ, яку потрібно видалити!
Тож не лише більшість інших відповідей потрапляють у цю небезпечну пастку, навіть дуже популярні git
розширення мали цю помилку ( вона зараз виправлена )! Тож краще тримати свої .git/
довідники, якщо ви точно не робите, що робите!
І з філософського погляду витирання історії завжди неправильне!
За винятком квантової механіки , як завжди, але це щось зовсім інше.
FYI ви, напевно, здогадалися: hilbix - це мій обліковий запис GitHub.
git rm modulename
іrm -rf .git/modules/modulename