Comment trouver tous les dépôts git dans des dossiers donnés (rapide)

9

L'approche naïve est find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} , mais c'est trop lent pour moi, car j'ai des structures de dossiers très profondes dans les dépôts git (du moins je pense que c'est la raison). J'ai lu à propos de cela que je peux utiliser prunepour empêcher find de récurser dans les répertoires une fois qu'il a trouvé quelque chose, mais il y a deux choses. Je ne sais pas comment cela fonctionne (je veux dire que je ne comprends pas ce qui se prunepasse bien que j'aie lu la page de manuel) et la seconde, cela ne fonctionnerait pas dans mon cas, car cela empêcherait findde rentrer dans le .gitdossier mais pas dans tous d'autres dossiers.

Donc ce dont j'ai réellement besoin c'est:

pour tous les sous-répertoires, vérifiez s'ils contiennent un .gitdossier et si c'est le cas, arrêtez de chercher dans cette branche de système de fichiers et rapportez le résultat. Ce serait parfait si cela excluait également les répertoires cachés de la recherche.

user1685095
la source

Réponses:

8

D'accord, je ne sais toujours pas totalement comment cela fonctionne, mais je l'ai testé et cela fonctionne.

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

6 directories, 0 files

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

J'ai hâte de faire la même chose plus rapidement.

user1685095
la source
2
De -prunecette façon: vous commencez à la racine d'un arbre, vous le descendez et lorsqu'une certaine condition s'applique, vous coupez un sous-arbre entier (comme un véritable "élagage"), donc vous ne regarderez plus de nœuds dans ce sous-arbre .
phk
@phk oh, merci. J'ai l'impression de le saisir maintenant. Nous recherchons les répertoires -type dpour quelle condition test -e ...est vraie et si elle est vraie, nous exécutons des actions -print -prunequi signifient l'imprimer et couper le sous-arbre, non?
user1685095
Oui, nous coupons le sous-arbre dont il est la racine.
phk
Un rapide pour utiliser votre solution pour "mettre à jour" tous les git repos: find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU parallelest un remplacement très pratique pourxargs
Marcello Romani
vous n'aurez pas de sous-modules, qui sont aussi des git repos. Vous voudrez peut-être les récupérer en récupérant récursivement des sous-modules, une fois que la liste root-repos est retournée par cette commande.
hoijui
2

Solution possible

Pour GNU findet autres implémentations qui prennent en charge -execdir:

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

(voir les commentaires)

Trucs discutés précédemment

Solution si l'élagage ci .git- dessous est suffisant

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

Si -printf '%h'est pris en charge (comme dans le cas des GNU find), nous n'avons pas besoin dirname:

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

Une fois qu'il rencontre un dossier .gitdans le chemin actuel, il le sortira et cessera de regarder plus loin dans le sous-arbre.

Solution si l'arborescence de dossiers entière doit être élaguée une fois a .gittrouvé

Utilisation -quitsi votre findsupport:

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

(Selon ce post détaillé de Stéphane Chazelas -quit est pris en charge dans GNU et FreeBSD findet dans NetBSD as -exit.)

Encore une fois avec -printf '%h'si pris en charge:

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

Solution d'élagage au même niveau que l'emplacement du .gitdossier

Voir la partie «Solution possible» pour la solution actuelle de ce problème particulier.

(Oh et évidemment, les solutions utilisant xargssupposent qu'il n'y a pas de nouvelle ligne dans les chemins, sinon vous auriez besoin d'une magie à octets nuls.)

phk
la source
si dir1contient deux répertoires dirxet diryque chacun contient un .gitrépertoire, cela ne signale que dirx/.git
iruvar
@iruvar Ah OK, je vous ai mal compris dans ce cas, j'essaierai alors de refaire la solution.
phk
le problème avec votre nouvelle solution est que si elle dir1/.gitexiste, elle descend toujours dir1/dirx, ce qui, sur la base de ma lecture de l'exigence d'OP, n'est pas souhaité
iruvar
@iruvar OK, a ajouté cela également. Avez-vous d'autres idées sur ce que l'OP aurait pu signifier? ;-)
phk
@iruvar exactement
user1685095
2

Idéalement, vous voudriez explorer les arborescences de répertoires pour les répertoires qui contiennent une .gitentrée et arrêter de chercher plus loin dans ces répertoires (en supposant que vous n'avez plus de git repos dans git repos).

Le problème est qu'avec le standard find, faire ce genre de vérification (qu'un répertoire contient une .gitentrée) implique de générer un processus qui exécute un testutilitaire en utilisant le -execprédicat, ce qui sera moins efficace que de lister le contenu de quelques répertoires.

Une exception serait si vous utilisez la fonction findintégrée du boshshell (un fork POSIXified du shell Bourne développé par @schily ) qui a un -callprédicat pour évaluer le code dans le shell sans avoir à générer un nouvel interpréteur sh:

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

Ou l' utilisation perlde 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' .

Plus, mais plus vite que zshl « printf '%s\n' **/.git(:h)(qui descend dans tous les répertoires non cachés), ou GNU find» s find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -printqui exécute une testcommande dans un nouveau processus pour chaque répertoire non caché.

Stéphane Chazelas
la source
1
Notez que cela .gitpeut aussi être un fichier - viagit worktree
Steven Penny
1
Merci @StevenPenny, je n'étais pas au courant de cela. J'ai maintenant changé le -ds en -e.
Stéphane Chazelas
1

Si vous utilisez Locate, vous pouvez trouver des répertoires avec:

locate .git | grep "/.git$"

La liste des résultats est rapide et le traitement ultérieur est également facile.

Jarivaa
la source
2
locate '*/.git'devrait suffire.
Stéphane Chazelas
0

Utilisation

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

timecela, pour voir la différence avec et sans -prune.

Ceci est basé sur une solution dans le man find. Vous pouvez modifier le CVSet svnsi ce n'est pas nécessaire. le contenu de la page de manuel suit

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

Étant donné le répertoire de projets suivant et les répertoires administratifs SCM associés, effectuez une recherche efficace des racines des projets:

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

Dans cet exemple, -pruneempêche la descente inutile dans des répertoires qui ont déjà été découverts (par exemple, nous ne recherchons pas project3/src, car nous avons déjà trouvé project3/.svn), mais garantit que les répertoires frères ( project2et project3) sont trouvés.

pingouin tranquille
la source