A beneficio del lettore, questo qui cerca di riassumere e fornire una guida passo-passo su come farlo se le cose non funzionano come previsto. Di seguito è riportato il modo testato e sicuro per la git
versione 2.17
e successive per sbarazzarsi di un sottomodulo :
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
- Se questo non funziona per te, vedi sotto.
- Nessuna opzione Niente di pericoloso. E non considerare nemmeno di fare di più!
- Testato con Debian Buster
2.20.1
e Ubuntu 18.04 2.17.1
.
"$submodule"
è solo per sottolineare dove mettere il nome e che devi stare attento con spazi e simili
- Se su Windows ignora la prima riga e sostituisci
"$submodule"
con il modo Windows di un percorso correttamente specificato al sottomodulo. (Non sono Windows)
Avvertimento!
Non toccare mai l'interno della .git
directory da soli! La modifica all'interno .git
entra nel lato oscuro. Stai lontano a tutti i costi!
E sì, puoi dare la colpa git
a questo, dato che molte cose utili mancavano in git
passato. Come un modo corretto per rimuovere nuovamente i sottomoduli.
Penso che ci sia una parte molto pericolosa nella documentazione di git submodule
. Si consiglia di rimuovere $GIT_DIR/modules/<name>/
te stesso.
Secondo me questo non è solo un errore, ma è estremamente pericoloso e provoca in futuro grossi mal di testa! Vedi sotto.
Nota che
git module deinit
è l'inverso diretto a
git module init
ma
git submodule deinit -- module
git rm -- module
inoltre è abbastanza il contrario
git submodule add -- URL module
git submodule update --init --recursive -- module
perché alcuni comandi in pratica devono fare più di una sola cosa:
git submodule deinit -- module
- (1) aggiornamenti
.git/config
git rm
- (2) rimuove i file del modulo
- (3) rimuove in tal modo ricorsivamente i sottomoduli del sottomodulo
- (4) aggiornamenti
.gitmodules
git submodule add
- inserisce i dati in
.git/modules/NAME/
- (1)
git submodule init
sì, quindi aggiorna.git/config
- (2)
git submodule update
, quindi, controlla in modo non ricorsivo il modulo
- (4) aggiornamenti
.gitmodules
git submodule update --init --recursive -- module
- inserisce ulteriori dati se necessario
- (3) verifica ricorsivamente i sottomoduli del sottomodulo
Questo non può essere completamente simmetrico, poiché mantenerlo rigorosamente simmetrico non ha molto senso. Semplicemente non sono necessari più di due comandi. Anche "estrarre i dati" è implicito, perché ne hai bisogno, ma non è possibile rimuovere le informazioni memorizzate nella cache, perché non sono affatto necessarie e potrebbero cancellare dati preziosi.
Questo è davvero sconcertante per i nuovi arrivati, ma fondamentalmente è una buona cosa: git
fa semplicemente la cosa ovviamente e fa bene, e non cerca nemmeno di fare di più. git
è uno strumento che deve fare un lavoro affidabile, invece di essere solo un altro "Eierlegende Wollmilchsau" ("Eierlegende Wollmilchsau" si traduce per me in "una versione malvagia di un coltellino svizzero").
Quindi capisco le lamentele delle persone, dicendo "Perché non fa git
la cosa ovvia per me". Questo perché "ovvio" qui dipende dal punto di vista. L'affidabilità in ogni situazione è molto più importante. Quindi ciò che è ovvio per te spesso non è la cosa giusta in tutte le possibili situazioni tecniche. Ricorda che: AFAICS git
segue il percorso tecnico, non quello sociale. (Da qui il nome intelligente: git)
Se questo fallisce
I comandi precedenti potrebbero non riuscire a causa di:
- Sei
git
troppo vecchio. Quindi utilizzare un nuovogit
. (Vedi sotto come.)
- Hai dati non impegnati e potresti perdere dati. Quindi meglio impegnarli prima.
- Il tuo sottomodulo non è pulito in a
git clean
certo senso. Quindi prima pulisci il tuo sottomodulo usando quel comando. (Vedi sotto.)
- In passato hai fatto qualcosa che non è supportato da
git
. Allora sei sul lato oscuro e le cose si fanno brutte e complicate. (Forse usando un'altra macchina lo risolve.)
- Forse ci sono altri modi per fallire di cui non sono a conoscenza (sono solo un po 'di
git
power-user.)
Seguono possibili correzioni.
Usa un nuovo git
Se la tua macchina è troppo vecchia non ce n'è submodule deinit
nella tua git
. Se non vuoi (o puoi) aggiornare il tuo git
, usa semplicemente un'altra macchina con una nuova git
! git
è pensato per essere completamente distribuito, quindi puoi usarne un altro git
per completare il lavoro:
workhorse:~/path/to/worktree$ git status --porcelain
non deve emettere nulla! In tal caso, prima pulisci le cose!
workhorse:~/path/to/worktree$ ssh account@othermachine
othermachine:~$ git clone --recursive me@workhorse path/to/worktree/.git TMPWORK && cd TMPWORK
- Ora fai le cose del sottomodulo
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 questo non funziona, utilizzaregit reset --soft FETCH_HEAD
- Ora
git status
pulisci le cose, fino a quando non è di nuovo pulito. Sei in grado di farlo, perché l'hai già pulito prima, grazie al primo passo.
Questo othermachine
può essere un po 'di VM, o qualche Ubuntu WSL in ambiente Windows, a prescindere. Anche un chroot
(ma presumo che tu non sia root, perché se lo sei root
dovrebbe essere più facile aggiornarlo al più recente git
).
Nota che se non riesci ssh
a entrare, ci sono un sacco di modi per trasportare i git
repository. Puoi copiare il tuo worktree su una chiavetta USB (compresa la .git
directory) e clonarlo dalla chiavetta. Clona la copia, solo per riportare le cose in modo pulito. Questo potrebbe essere un PITA, nel caso in cui i sottomoduli non siano accessibili direttamente da altre macchine. Ma c'è anche una soluzione per questo:
git config --add url.NEWURLPREFIX.insteadOf ORIGINALURLPREFIX
È possibile utilizzare questo moltiplicare e questo viene salvato in $HOME/.gitconfig
. Qualcosa di simile a
git config --add 'url./mnt/usb/repo/.insteadof' https://github.com/
riscrive URL come
https://github.com/XXX/YYY.git
in
/mnt/usb/repo/XXX/YYY.git
È facile se inizi ad abituarti a potenti git
funzionalità come questa.
Pulisci prima le cose
La pulizia manuale è buona, perché in questo modo potresti forse rilevare alcune cose che hai dimenticato.
- Se git si lamenta di cose non salvate, commetti e spingile in un posto sicuro.
- Se git si lamenta di alcuni avanzi
git status
ed git clean -ixfd
è tuo amico
- Cerca di astenersi dalle opzioni per
rm
e il deinit
più a lungo possibile. Le opzioni (come -f
) per git
sono buone se sei un professionista. Ma come sei venuto qui, probabilmente non sei così esperto nella submodule
zona. Quindi meglio prevenire che curare.
Esempio:
$ 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'
Vedi, non ce n'è -f
bisogno submodule deinit
. Se le cose sono pulite, in un git clean
certo senso. Si noti inoltre che git clean -x
non è necessario. Ciò significa che git submodule deinit
rimuove incondizionatamente i file non monitorati che vengono ignorati. Questo è di solito quello che vuoi, ma non dimenticartene. A volte i file ignorati possono essere preziosi, come i dati memorizzati nella cache che richiedono ore o giorni per essere nuovamente calcolati.
Perché non rimuoverlo mai $GIT_DIR/modules/<name>/
?
Probabilmente le persone vogliono rimuovere il repository memorizzato nella cache, perché hanno paura di incorrere in un problema in seguito. Questo è vero, ma imbattersi in quel "problema" è il modo corretto di risolverlo! Perché la correzione è facile e fatta bene sarai in grado di vivere per sempre felici e contenti. Ciò evita problemi più ingombranti rispetto a quando si rimuovono i dati da soli.
Esempio:
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
L'ultima riga genera il seguente errore:
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.
Perché questo errore? Perché in .git/modules/two/
precedenza era popolato da https://github.com/hilbix/empty.git e ora deve essere ripopolato da qualcos'altro, vale a dire https://github.com/hilbix/src.git . Non lo vedrai se lo popoli nuovamente da https://github.com/hilbix/empty.git
Cosa fare adesso? Bene, fai esattamente come detto! Uso--name someunusedname
git submodule add --name someunusedname https://github.com/hilbix/src.git two
.gitmodules
allora sembra
[submodule "someunusedname"]
path = two
url = https://github.com/hilbix/src.git
ls -1p .git/modules/
dà
someunusedname/
two/
In questo modo in futuro puoi cambiare succursale / impegnarti in avanti e indietro e non avrai mai più problemi , a causa di two/
due diversi repository (e forse incompatibili) a monte. E il migliore è: tieni anche entrambi memorizzati nella cache locale.
- Questo non è vero solo per te. È anche vero per tutti gli altri che usano il tuo repository.
- E non perdi la storia. Nel caso in cui ti sei dimenticato di inviare l'ultima versione del vecchio sottomodulo, puoi inserire la copia locale e farlo in seguito. Si noti che è abbastanza comune che qualcuno si dimentichi di inviare alcuni sottomoduli (perché questa è una PITA per i nuovi arrivati, fino a quando non si sono abituati
git
).
Tuttavia, se hai rimosso la directory memorizzata nella cache, entrambi i diversi checkout si imbatteranno l'uno nell'altro, perché non utilizzerai le --name
opzioni, giusto? Quindi ogni volta che fai il checkout forse devi rimuovere la .git/modules/<module>/
directory ancora e ancora. Questo è estremamente ingombrante e rende difficile usare qualcosa del genere git bisect
.
Quindi c'è un motivo molto tecnico per mantenere questa directory del modulo come segnaposto. Le persone che raccomandano di rimuovere qualcosa di seguito .git/modules/
o non sanno meglio o dimenticano di dirti che questo rende potenti funzionalità comegit bisect
quasi impossibili da usare se attraversasse una tale incompatibilità del sottomodulo.
Un ulteriore motivo è mostrato sopra. Guarda il ls
. Cosa vedi lì?
Bene, la seconda variante del modulo two/
non è sotto .git/modules/two/
, è sotto .git/modules/someunusedname/
! Quindi cose del genere git rm $module; rm -f .git/module/$module
sono totalmente sbagliate! È necessario consultare module/.git
o .gitmodules
per trovare la cosa giusta da rimuovere!
Quindi non solo la maggior parte delle altre risposte cade in questa pericolosa trappola, anche le git
estensioni molto popolari avevano questo bug ( ora è stato risolto lì )! Quindi meglio tenere le mani della .git/
directory se non esattamente, cosa stai facendo!
E dal punto di vista filosofico, cancellare la storia è sempre sbagliato!
Tranne la meccanica quantistica , come al solito, ma questo è qualcosa di completamente diverso.
Cordiali saluti, probabilmente lo hai indovinato: Hilbix è il mio account GitHub.
git rm modulename
erm -rf .git/modules/modulename