Para o benefício do leitor, isso aqui tenta resumir e fornecer um guia passo a passo sobre como fazê-lo, se as coisas não funcionarem conforme o esperado. A seguir, é apresentada a maneira testada e segura da gitversão 2.17e da versão anterior para se livrar de um submódulo :
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
- Se isso não funcionar, veja abaixo.
- Sem opções. Nada perigoso. E nem pense em fazer mais!
- Testado com Debian Buster
2.20.1e Ubuntu 18.042.17.1 .
"$submodule" é apenas enfatizar onde colocar o nome e que você deve ter cuidado com espaços e coisas do gênero
- Se no Windows, ignore a primeira linha e substitua
"$submodule"pela maneira do Windows de um caminho especificado corretamente para o submódulo. (Eu não sou Windows)
Atenção!
Nunca toque você mesmo no interior do .gitdiretório! Edição interna.git entra no lado escuro. Fique longe a todo custo!
E sim, você pode culpar gitpor isso, pois muitas coisas úteis estavam faltandogit passado. Como uma maneira adequada de remover submódulos novamente.
Eu acho que há uma parte muito perigosa na documentação do git submodule. Ele recomenda se remover $GIT_DIR/modules/<name>/.
No meu entendimento, isso não é apenas errado, é extremamente perigoso e provoca grandes dores de cabeça no futuro! Ver abaixo.
Observe que
git module deinit
é o inverso direto para
git module init
mas
git submodule deinit -- module
git rm -- module
também é bastante inverso a
git submodule add -- URL module
git submodule update --init --recursive -- module
porque alguns comandos basicamente precisam fazer mais do que apenas uma coisa:
git submodule deinit -- module
- (1) atualizações
.git/config
git rm
- (2) remove os arquivos do módulo
- (3) assim remove recursivamente os submódulos do submódulo
- (4) atualizações
.gitmodules
git submodule add
- puxa os dados para
.git/modules/NAME/
- (1) faz
git submodule init, então as atualizações.git/config
- (2) faz
git submodule update, portanto, check-out não-recursivo do módulo
- (4) atualizações
.gitmodules
git submodule update --init --recursive -- module
- extrai mais dados, se necessário
- (3) verifica os submódulos do submódulo recursivamente
Isso não pode ser totalmente simétrico, pois mantê-lo estritamente simétrico não faz muito sentido. Simplesmente não há necessidade de mais de dois comandos. Também "puxar os dados" está implícito, porque você precisa, mas a remoção das informações em cache não é feita, porque isso não é necessário e pode limpar dados preciosos.
Isso realmente é intrigante para os recém-chegados, mas basicamente é uma coisa boa: gitsimplesmente faz a coisa óbvia e faz o que é certo, e nem sequer tenta fazer mais. gité uma ferramenta que deve fazer um trabalho confiável, em vez de ser apenas mais um "Eierlegende Wollmilchsau" ("Eierlegende Wollmilchsau" significa para mim "uma versão maligna de um canivete suíço").
Então eu entendo as reclamações das pessoas, dizendo "Por que não faz gito óbvio por mim". Isso ocorre porque "óbvio" aqui depende do ponto de vista. Confiabilidade em cada situação é muito mais importante. Portanto, o que é óbvio para você muitas vezes não é a coisa certa em todas as situações técnicas possíveis. Lembre-se de que: AFAICSgit segue o caminho técnico, não o social. (Daí o nome inteligente: git)
Se isso falhar
Os comandos acima podem falhar devido ao seguinte:
- Você
gité muito velho. Então use um novogit . (Veja abaixo como.)
- Você possui dados não confirmados e pode perder dados. Então é melhor cometer primeiro.
- Seu submódulo não está limpo em um
git clean sentido. Em seguida, limpe seu submódulo usando esse comando. (Ver abaixo.)
- Você fez algo no passado que não é suportado por
git. Então você está no lado escuro e as coisas ficam feias e complicadas. (Talvez o uso de outra máquina conserte.)
- Talvez haja mais maneiras de falhar que eu não conheço (sou apenas um
gitusuário avançado).
Possíveis correções a seguir.
Use um mais novo git
Se a sua máquina for muito antiga, não há submodule deinitna sua git. Se você não deseja (ou pode) atualizar o seu git, basta usar outra máquina com uma nova git! gitdeve ser totalmente distribuído, para que você possa usar outro gitpara realizar o trabalho:
workhorse:~/path/to/worktree$ git status --porcelain não deve produzir nada! Se isso acontecer, limpe as coisas primeiro!
workhorse:~/path/to/worktree$ ssh account@othermachine
othermachine:~$ git clone --recursive me@workhorse path/to/worktree/.git TMPWORK && cd TMPWORK
- Agora faça o material do submódulo
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. Se isso não funcionar, usegit reset --soft FETCH_HEAD
- Agora limpe as coisas, até que
git statusesteja limpo novamente. Você pode fazer isso porque já o limpou antes, graças ao primeiro passo.
Esta othermachinepode ser uma VM, ou algum Ubuntu WSL no Windows, qualquer que seja. Mesmo um chroot(mas suponho que você não seja root, porque se for root, deve ser mais fácil atualizar para o mais novo git).
Observe que, se você não pode sshentrar, existem várias maneiras de transportar gitrepositórios. Você pode copiar sua árvore de trabalho em um pen drive (incluindo o .gitdiretório) e cloná-lo. Clone a cópia, apenas para obter as coisas de uma maneira limpa novamente. Pode ser uma PITA, caso seus submódulos não sejam acessíveis diretamente de outra máquina. Mas há uma solução para isso também:
git config --add url.NEWURLPREFIX.insteadOf ORIGINALURLPREFIX
Você pode usar isso multiplicar, e isso é salvo em $HOME/.gitconfig. Algo como
git config --add 'url./mnt/usb/repo/.insteadof' https://github.com/
reescreve URLs como
https://github.com/XXX/YYY.git
para dentro
/mnt/usb/repo/XXX/YYY.git
É fácil se você se acostumar a gitrecursos poderosos como esse.
Limpar as coisas primeiro
A limpeza manual é boa, pois dessa forma você talvez detecte algumas coisas que esqueceu.
- Se o git se queixar de coisas não salvas, faça o commit e leve-o para um local seguro.
- Se o git reclama de algumas sobras,
git statuse git clean -ixfdé seu amigo
- Tente se abster de opções para
rme deinitcontanto que você puder. Opções (como -f) para gitsão boas se você é um profissional. Mas como você veio aqui, provavelmente não tem tanta experiência na submoduleárea. Então é melhor prevenir do que remediar.
Exemplo:
$ 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'
Você vê, não há -fnecessidade submodule deinit. Se as coisas estão limpas, em certo git cleansentido. Observe também que git clean -xnão é necessário. Isso significa que git submodule deinitremove incondicionalmente arquivos não rastreados que são ignorados. Geralmente é isso que você deseja, mas não se esqueça disso. Às vezes, arquivos ignorados podem ser preciosos, como dados em cache, que levam horas ou dias para serem calculados novamente.
Por que nunca remover $GIT_DIR/modules/<name>/?
Provavelmente, as pessoas desejam remover o repositório em cache, porque têm medo de encontrar um problema posteriormente. Isso é verdade, mas encontrar esse "problema" é a maneira correta de resolvê-lo! Como a correção é fácil e bem feita, você será capaz de viver feliz para sempre. Isso evita problemas mais pesados do que quando você remove os dados por conta própria.
Exemplo:
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 última linha gera o seguinte erro:
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.
Por que esse erro? Como .git/modules/two/anteriormente foi preenchido em https://github.com/hilbix/empty.git e agora deve ser preenchido novamente de outra coisa, ou seja, https://github.com/hilbix/src.git . Você não verá isso se o preencher novamente em https://github.com/hilbix/empty.git
O que fazer agora? Bem, faça exatamente o que foi dito! Usar--name someunusedname
git submodule add --name someunusedname https://github.com/hilbix/src.git two
.gitmodules então parece
[submodule "someunusedname"]
path = two
url = https://github.com/hilbix/src.git
ls -1p .git/modules/ dá
someunusedname/
two/
Dessa forma, no futuro, você pode alternar entre ramificações / confirmação e avançar e nunca mais voltará a ter problemas , devido à two/existência de dois repositórios upstream diferentes (e possivelmente incompatíveis). E o melhor é: você também mantém os dois em cache localmente.
- Isso não é verdade apenas para você. Isso também é válido para todos os outros usuários do seu repositório.
- E você não perde a história. Caso você se esqueça de enviar a versão mais recente do submódulo antigo, poderá inserir a cópia local e fazê-lo posteriormente. Observe que é bastante comum que alguém esqueça de empurrar alguns submódulos (porque esse é um PITA para os recém-chegados, até que eles se acostumaram
git).
No entanto, se você removeu o diretório em cache, os dois caixas diferentes tropeçarão um no outro, porque você não usará as --nameopções, certo? Portanto, toda vez que você faz o checkout, talvez seja necessário remover o .git/modules/<module>/diretório várias vezes. Isso é extremamente complicado e dificulta o uso de algo parecido git bisect.
Portanto, há um motivo muito técnico para manter esse diretório de módulo como um espaço reservado. As pessoas que recomendam remover algo abaixo .git/modules/não sabem melhor ou esquecem de informar que isso cria recursos poderosos, comogit bisect quase impossíveis o uso de se isso ultrapassar uma incompatibilidade desse sub-módulo.
Uma outra razão é mostrada acima. Olhe para o ls. O que você vê lá?
Bem, a segunda variante do módulo two/não está abaixo .git/modules/two/, está abaixo .git/modules/someunusedname/! Então, coisas como git rm $module; rm -f .git/module/$moduleestão totalmente erradas! Você deve consultar module/.gitou .gitmodulesencontrar a coisa certa a remover!
Portanto, não apenas a maioria das outras respostas cai nessa perigosa armadilha, mas mesmo as gitextensões muito populares tinham esse bug ( agora está corrigido aqui )! Portanto, é melhor manter as mãos no .git/diretório, se você não fizer exatamente o que está fazendo!
E do ponto de vista filosófico, limpar a história está sempre errado!
Exceto pela mecânica quântica , como sempre, mas isso é algo completamente diferente.
Para sua informação, você provavelmente adivinhou: hilbix é minha conta do GitHub.
git rm modulenameerm -rf .git/modules/modulename