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 git
versão 2.17
e 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.1
e 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 .git
diretório! Edição interna.git
entra no lado escuro. Fique longe a todo custo!
E sim, você pode culpar git
por 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: git
simplesmente 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 git
o ó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
git
usuá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 deinit
na sua git
. Se você não deseja (ou pode) atualizar o seu git
, basta usar outra máquina com uma nova git
! git
deve ser totalmente distribuído, para que você possa usar outro git
para 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 status
esteja limpo novamente. Você pode fazer isso porque já o limpou antes, graças ao primeiro passo.
Esta othermachine
pode 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 ssh
entrar, existem várias maneiras de transportar git
repositórios. Você pode copiar sua árvore de trabalho em um pen drive (incluindo o .git
diretó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 git
recursos 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 status
e git clean -ixfd
é seu amigo
- Tente se abster de opções para
rm
e deinit
contanto que você puder. Opções (como -f
) para git
sã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á -f
necessidade submodule deinit
. Se as coisas estão limpas, em certo git clean
sentido. Observe também que git clean -x
não é necessário. Isso significa que git submodule deinit
remove 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 --name
opçõ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/$module
estão totalmente erradas! Você deve consultar module/.git
ou .gitmodules
encontrar a coisa certa a remover!
Portanto, não apenas a maioria das outras respostas cai nessa perigosa armadilha, mas mesmo as git
extensõ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 modulename
erm -rf .git/modules/modulename