В интересах читателя, здесь делается попытка подвести итоги и дать пошаговое руководство о том, как это сделать, если все работает не так, как ожидалось. Ниже приведен проверенный и безопасный способ для git
версии 2.17
и выше избавиться от подмодуля :
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
- Если это не работает для вас, см. Ниже.
- Нет вариантов. Ничего опасного. И даже не думай делать больше!
- Протестировано с Debian Buster
2.20.1
и Ubuntu 18.042.17.1
.
"$submodule"
просто чтобы подчеркнуть, где поставить имя, и что вы должны быть осторожны с пробелами и тому подобное
- Если в Windows пропустите первую строку и замените
"$submodule"
на путь 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
может быть какая-то виртуальная машина или Ubuntu WSL под Windows, что угодно. Даже a chroot
(но я предполагаю, что вы не являетесь пользователем root, потому что если 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 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/
наличия двух разных (и, возможно, несовместимых) вышестоящих репозиториев. И самое лучшее: вы храните оба кэшированных локально.
- Это касается не только вас. Это также верно для всех остальных, использующих ваш репозиторий.
- И вы не потеряете историю. Если вы забыли загрузить самую последнюю версию старого субмодуля, вы можете ввести локальную копию и сделать это позже. Обратите внимание, что довольно часто кто-то забывает подтолкнуть некоторые подмодули (потому что это PITA для новичков, пока они не привыкли
git
).
Однако, если вы удалили кэшированный каталог, обе разные проверки наткнуться друг на друга, потому что вы не будете использовать --name
параметры, верно? Поэтому каждый раз, когда вы делаете заказ, вам, возможно, придется .git/modules/<module>/
снова и снова удалять каталог. Это чрезвычайно громоздко и затрудняет использование чего-то подобного git bisect
.
Таким образом, существует техническая причина сохранить этот каталог модулей в качестве заполнителя. Люди, которые рекомендуют удалить что-то ниже, .git/modules/
либо не знают лучше, либо забывают сказать вам, что это делает git bisect
практически невозможным использование таких мощных функций, как, например, несовместимость субмодулей.
Еще одна причина показана выше. Посмотрите на ls
. Что ты там видишь?
Ну, второй вариант модуля two/
не под .git/modules/two/
, он под .git/modules/someunusedname/
! Так что такие вещи git rm $module; rm -f .git/module/$module
совершенно не так! Вы должны либо проконсультироваться, module/.git
либо .gitmodules
найти нужную вещь для удаления!
Так что не только большинство других ответов попадают в эту опасную ловушку, но даже очень популярные git
расширения имели эту ошибку ( теперь она исправлена )! Так что лучше держите руки в .git/
справочнике, если не совсем точно, что вы делаете!
И с философской точки зрения вытирая историю всегда неправильно!
За исключением квантовой механики , как обычно, но это нечто совершенно другое.
К вашему сведению, вы, наверное, догадались: hilbix - это мой аккаунт на GitHub.
git rm modulename
иrm -rf .git/modules/modulename