Como encontrar todos os repositórios git em determinadas pastas (rápido)

9

A abordagem ingênua é find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} , mas é muito lenta para mim, porque eu tenho muitas estruturas de pastas profundas nos repositórios git (pelo menos eu acho que esse é o motivo). Eu li sobre isso que posso usar prunepara impedir que o find seja recursivo em diretórios, uma vez que ele encontrou algo, mas há duas coisas. Não sei ao certo como isso funciona (quero dizer, não entendo o que prunefaz, embora tenha lido a página de manual) e o segundo não funcionaria no meu caso, porque impediria findrecursão na .gitpasta, mas não em todos outras pastas.

Então, o que eu realmente preciso é:

para todos os subdiretórios, verifique se eles contêm uma .gitpasta e se é, então pare de procurar nesta ramificação do sistema de arquivos e relate o resultado. Seria perfeito se isso também excluísse qualquer diretório oculto da pesquisa.

user1685095
fonte

Respostas:

8

Ok, ainda não tenho muita certeza de como isso funciona, mas eu testei e funciona.

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

6 directories, 0 files

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

Estou ansioso para fazer o mesmo mais rápido.

user1685095
fonte
2
Desta -prunemaneira: você começa na raiz de uma árvore e a move para baixo; quando uma determinada condição se aplica, você corta uma subárvore inteira (como uma "poda real"), para que não veja mais nós nessa subárvore .
Phd
@phk oh, obrigado. Eu pareço entender isso agora. Estamos pesquisando nos diretórios -type dqual condição test -e ...é verdadeira e, se for verdade, executamos ações -print -pruneque significam imprimi-la e cortar subárvore, certo?
user1685095
Sim, cortamos a subárvore da qual é a raiz.
Phk
Um rápido para usar a sua solução para "atualizar" todos os repos git: find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU parallelé um substituto muito útil paraxargs
Marcello Romani
você não receberá submódulos, que também são repositórios git. Convém buscá-los, buscando recursivamente submódulos, depois de ter a lista de repositórios raiz retornada por este comando.
hoijui 15/04
2

Solução possível

Para GNU finde outras implementações que suportam -execdir:

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

(veja os comentários)

Coisas discutidas anteriormente

Solução se a poda abaixo .gitfor suficiente

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

Se -printf '%h'for suportado (como no caso do GNU find), não precisamos dirname:

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

Depois de encontrar uma pasta .gitno caminho atual, ela será impressa e depois deixará de olhar mais abaixo na subárvore.

Solução para remover toda a árvore de pastas quando .gitfor encontrado

Usando -quitse o seu findsuporte:

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

(De acordo com este post detalhado de Stéphane Chazelas -quit é suportado no GNU e no FreeBSD finde no NetBSD como -exit.)

Novamente com -printf '%h'se suportado:

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

Solução para poda no mesmo nível em que a .gitpasta está

Consulte a parte "Solução possível" para obter a solução atual para esse problema específico.

(Ah, e obviamente as soluções usando xargsassumem que não há novas linhas nos caminhos, caso contrário, você precisaria de magia de bytes nulos.)

phk
fonte
se dir1contém dois diretórios dirxe dirycada um contém um .gitdiretório, isso somente informa dirx/.git
iruvar
@iruvar Ah OK, eu entendi errado você, nesse caso, vou tentar refazer a solução então.
Php
o problema com a sua nova solução é essa, se dir1/.gitexiste, ele ainda desce dir1/dirx, que, com base na minha leitura da exigência do OP, não é desejado
Iruvar
@iruvar OK, acrescentou isso também. Alguma outra idéia sobre o que OP poderia ter significado? ;-)
phk
@iruvar exatamente
user1685095
2

Idealmente, você deseja rastrear as árvores de diretórios em busca de diretórios que contenham uma .gitentrada e parar de pesquisar mais abaixo (supondo que você não tenha mais repositórios git dentro dos repositórios git).

O problema é que, com o padrão find, fazer esse tipo de verificação (se um diretório contém uma .gitentrada) envolve gerar um processo que executa um testutilitário usando o -execpredicado, o que será menos eficiente do que listar o conteúdo de alguns diretórios.

Uma exceção seria se você usasse o built-in finddo boshshell (um fork POSIXified do shell Bourne desenvolvido por @schily ) que possui um -callpredicado para avaliar o código no shell sem precisar gerar um novo interpretador sh:

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

Ou o uso 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' .

Mais longo, mas mais rápido do que zsh's printf '%s\n' **/.git(:h)(que desce em todos os diretórios não ocultos), ou GNU find' s find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -printque corre um testcomando em um novo processo para cada diretório não oculta.

Stéphane Chazelas
fonte
1
Observe que também .gitpode ser um arquivo - viagit worktree
Steven Penny
1
Obrigado @StevenPenny, eu não estava ciente disso. Agora mudei os -dpara -e.
Stéphane Chazelas
1

Se você usar o localizador, poderá encontrar diretórios com:

locate .git | grep "/.git$"

A lista de resultados é rápida e o processamento adicional também é fácil.

Jarivaa
fonte
2
locate '*/.git'deve ser suficiente.
Stéphane Chazelas
0

Usar

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

timeisso, para ver a diferença com e sem -prune.

Isso é baseado em uma solução no man find. Você pode editar CVSe, svnse não for necessário. o conteúdo da página de manual segue

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

Dado o seguinte diretório de projetos e seus diretórios administrativos associados do SCM, execute uma pesquisa eficiente pelas raízes dos projetos:

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

Neste exemplo, -pruneevita a descida desnecessária em diretórios que já foram descobertos (por exemplo, não pesquisamos project3/src, porque já encontramos project3/.svn), mas assegura a localização de diretórios irmãos ( project2e project3).

quiet_penguin
fonte