Come trovare tutti i repository git all'interno di determinate cartelle (velocemente)


9

L'approccio ingenuo è find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} , ma è troppo lento per me, perché ho strutture di cartelle molto profonde all'interno dei repository git (almeno penso che questo sia il motivo). Ho letto che posso usare pruneper impedire a find di ricorrere nelle directory una volta che ha trovato qualcosa, ma ci sono due cose. Non sono sicuro di come prunefunzioni (voglio dire, non capisco cosa funziona nonostante abbia letto la pagina man) e il secondo non funzionerebbe nel mio caso, perché impedirebbe finddi ricorrere nella .gitcartella ma non in tutto altre cartelle.

Quindi quello di cui ho davvero bisogno è:

per tutte le sottodirectory controllare se contengono una .gitcartella e se è quindi interrompere la ricerca in questo ramo del filesystem e riportare il risultato. Sarebbe perfetto se questo escludesse anche qualsiasi directory nascosta dalla ricerca.



Risposte:


8

D'accordo, non sono ancora del tutto sicuro di come funzioni, ma l'ho provato e funziona.

.
├── a
│   ├── .git
│   └── a
│       └── .git
└── b
    └── .git

6 directories, 0 files

% find . -type d -exec test -e '{}/.git' ';' -print -prune
./a
./b

Non vedo l'ora di fare lo stesso più velocemente.


2
In -prunequesto modo: inizi dalla radice di un albero, lo sposti e quando si applica una determinata condizione, tagli di un'intera sottostruttura (come una vera "potatura"), quindi non guarderai più nodi in questa sottostruttura .
phk,

@phk oh, grazie. Mi sembra di afferrarlo ora. Stiamo cercando directory -type dper cui la condizione test -e ...è vera e se è vera eseguiamo azioni -print -pruneche significa stamparla e tagliare sottostruttura, giusto?
user1685095

Sì, tagliamo la sottostruttura di cui è la radice.
phk,

Uno veloce per usare la tua soluzione per "aggiornare" tutti i repository git: find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU parallelè un sostituto molto utile perxargs
Marcello Romani,

non otterrai sub-moduli, che sono anche repository git. Potrebbe essere necessario recuperarli recuperando in modo ricorsivo i sottomoduli, una volta ottenuto l'elenco di repository root restituito da questo comando.
hoijui,

2

Possibile soluzione

Per GNU finde altre implementazioni che supportano -execdir:

find dir1 dir2 dir3 -type d -execdir test -d '.git' \; -print -prune

(vedi i commenti)

Roba precedentemente discussa

Soluzione se la potatura di seguito .gitè sufficiente

find dir1 dir2 dir3 -type d -path '*/.git' -print -prune | xargs -I {} dirname {}

Se -printf '%h'è supportato (come nel caso di GNU find) non abbiamo bisogno di dirname:

find dir1 dir2 dir3 -type d -path '*/.git' -printf '%h\n' -prune

Una volta che incontra una cartella .gitnel percorso corrente, la emetterà e poi smetterà di guardare più in basso nella sottostruttura.

Soluzione se è necessario eliminare l'intero albero delle cartelle una volta .gittrovato a

Usando -quitse findlo supporta:

for d in dir1 dir2 dir3; do
  find "$d" -type d -name .git -print -quit
done | xargs -I {} dirname {}

(Secondo questo post dettagliato di Stéphane Chazelas -quit è supportato in GNU e FreeBSD finde in NetBSD come -exit.)

Ancora con -printf '%h'se supportato:

for d in dir1 dir2 dir3; do
  find "$d" -type d -name .git -printf '%h\n' -quit
done

Soluzione per la potatura allo stesso livello della .gitcartella

Vedere la parte "Soluzione possibile" per la soluzione corrente per questo particolare problema.

(Oh, e ovviamente le soluzioni che usano xargspresuppongono che non ci siano nuove linee nei percorsi, altrimenti avresti bisogno di magia a byte zero.)


se dir1contiene due directory dirxe diryognuna contiene una .gitdirectory, questo riporta solo dirx/.git
iruvar il

@iruvar Ah OK, in questo caso ti ho frainteso, quindi proverò a ripetere la soluzione.
phk,

il problema con la tua nuova soluzione è questo, se dir1/.gitesiste, scende ancora dir1/dirx, che, in base alla mia lettura dei requisiti del PO, non è desiderato
iruvar

@iruvar OK, ha aggiunto anche quello. Altre idee su cosa avrebbe potuto significare OP? ;-)
phk il

@iruvar esattamente
user1685095

2

Idealmente, si desidera eseguire la scansione degli alberi di directory per le directory che contengono una .gitvoce e interrompere la ricerca più in basso (supponendo che non si disponga di ulteriori repository git all'interno dei repository git).

Il problema è che con lo standard find, fare questo tipo di controllo (che una directory contenga una .gitvoce) implica la generazione di un processo che esegue testun'utilità usando il -execpredicato, che sarà meno efficiente rispetto all'elenco del contenuto di alcune directory.

Un'eccezione sarebbe se si utilizza l' findintegrato della boshshell (un fork POSIXified della shell Bourne sviluppato da @schily ) che ha un -callpredicato per valutare il codice nella shell senza dover generare un nuovo interprete sh:

#! /path/to/bosh
find . -name '.?*' -prune -o \
  -type d -call '[ -e "$1/.git" ]' {} \; -prune -print

O l'uso perl's File::Find:

perl -MFile::Find -le '
  sub wanted {
    if (/^\../) {$File::Find::prune = 1; return}
    if (-d && -e "$_/.git") {
       print $File::Find::name; $File::Find::prune = 1
    }
  }; find \&wanted, @ARGV' .

Più a lungo, ma più veloce di zsh's printf '%s\n' **/.git(:h)(che scende in tutte le directory non nascoste), o GNU find' s find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -printche corre un testcomando in un nuovo processo per ogni directory non nascoste.


1
Si noti che .gitpuò essere anche un file - tramitegit worktree
Steven Penny,

1
Grazie @StevenPenny, non ne ero a conoscenza. Ora ho cambiato la -ds in -e.
Stéphane Chazelas,

1

Se si utilizza individuare, è possibile trovare directory con:

locate .git | grep "/.git$"

L'elenco dei risultati è veloce e anche l'elaborazione ulteriore è facile.


2
locate '*/.git'dovrebbe essere abbastanza.
Stéphane Chazelas,

0

Uso

find ~/GIT-REPOSITORIES \( -exec test -d '{}'/.git \; \) -print -prune

timequesto, per vedere la differenza con e senza -prune.

Questo si basa su una soluzione in man find. È possibile modificare il CVSe svnse non richiesto. segue il contenuto della pagina man

find repo/ \( -exec test -d '{}'/.svn \; -or \
       -exec test -d {}/.git \; -or -exec test -d {}/CVS \; \) \
       -print -prune

Dato il seguente elenco di progetti e le relative directory amministrative SCM, eseguire una ricerca efficiente delle radici dei progetti:

repo/project1/CVS
repo/gnu/project2/.svn
repo/gnu/project3/.svn
repo/gnu/project3/src/.svn
repo/project4/.git

In questo esempio, -pruneimpedisce la discesa non necessaria nelle directory che sono già state scoperte (ad esempio, non effettuiamo ricerche project3/src, perché abbiamo già trovato project3/.svn), ma assicura project2che project3vengano trovate le directory dei fratelli ( e ).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.