Как найти все git-репозитории в указанных папках (быстро)

10

Наивный подход есть find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} , но он слишком медленный для меня, потому что у меня много глубоких структур папок внутри git-репозиториев (по крайней мере, я думаю, что это причина). Я читал о том, что могу использовать, pruneчтобы предотвратить поиск в каталогах, как только он что-то найдет, но есть две вещи. Я не уверен, как это работает (я имею в виду, я не понимаю, что pruneделает, хотя я прочитал man-страницу), и во-вторых, это не сработает в моем случае, потому что это помешает findвернуться в .gitпапку, но не во все другие папки.

Так что мне действительно нужно:

для всех подкаталогов проверьте, содержат ли они .gitпапку, а затем остановите поиск в этой ветви файловой системы и сообщите результат. Было бы идеально, если бы это также исключало любые скрытые каталоги из поиска.

user1685095
источник
1
getpof .gitэто то, что я использую. github.com/thrig/scripts/blob/master/filesys/getpof.c
августа

Ответы:

9

Хорошо, я все еще не совсем уверен, как это работает, но я проверил это, и это работает.

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

6 directories, 0 files

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

Я с нетерпением жду, чтобы сделать то же самое быстрее.

user1685095
источник
3
Это -pruneтак: вы начинаете с корня дерева, перемещаете его вниз, а когда применяется определенное условие, вы обрезаете целое поддерево (например, «обрезка»), поэтому вы больше не будете смотреть на другие узлы в этом поддереве. ,
phk
@ phk О, спасибо. Кажется, я понял это сейчас. Мы ищем каталоги, -type dдля которых условие test -e ...истинно, и если оно истинно, мы выполняем действия, -print -pruneчто означает печать и вырезание поддерева, верно?
user1685095 31.12.16
Да, мы вырезали поддерево, из которого он является корнем.
phk
Быстрый способ использовать ваше решение для «обновления» всех репозиториев git: find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU parallel- очень удобная заменаxargs
Marcello Romani
вы не получите субмодули, которые также являются git-репозиториями. Возможно, вы захотите получить их путем рекурсивной выборки подмодулей, как только у вас будет список корневых репозиториев, возвращаемый этой командой.
Hoijui
2

Возможное решение

Для GNU findи других реализаций, которые поддерживают -execdir:

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

(см. комментарии)

Ранее обсужденные вещи

Решение, если обрезка ниже .gitдостаточно

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

Если -printf '%h'поддерживается (как в случае с GNU find), нам не нужно dirname:

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

Как только он находит папку .gitв текущем пути, он выводит ее, а затем перестает смотреть дальше вниз по поддереву.

Решение, если все дерево папок должно быть удалено, как только .gitнайден

Используя, -quitесли ваш findподдерживает это:

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

(Согласно этому подробному сообщению Стефана Шазеласа -quit поддерживается в GNU и FreeBSD, findа также в NetBSD -exit.)

Снова, -printf '%h'если поддерживается:

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

Решение для обрезки на том же уровне, что и .gitпапка

См. Часть «Возможное решение» для текущего решения этой конкретной проблемы.

(О, и очевидно, что решения, использующие, xargsпредполагают, что в путях нет новых строк, в противном случае вам понадобится магия с нулевым байтом.)

PHK
источник
если dir1содержит две директории dirxи diryкаждая из них содержит .gitдиректорию, это только отчеты dirx/.git
iruvar
@iruvar Ах, хорошо, я не понял вас в этом случае, я постараюсь переделать решение тогда.
phk
проблема с вашим новым решением заключается в том dir1/.git, что если оно существует, оно все еще спускается dir1/dirx, что, исходя из моего прочтения требования ОП, нежелательно
iruvar
@iruvar Хорошо, добавил это тоже. Любые другие идеи о том, что ОП мог бы означать? ;-)
phk
@iruvar точно
user1685095
2

В идеале вы должны сканировать деревья каталогов для каталогов, которые содержат .gitзаписи, и прекращать поиск дальше по ним (при условии, что у вас нет дополнительных репозиториев git внутри репозиториев git).

Проблема состоит в том, что при стандартной findпроверке такого типа (что каталог содержит .gitзапись) требуется создание процесса, который выполняет testутилиту с использованием -execпредиката, что будет менее эффективно, чем перечисление содержимого нескольких каталогов.

Исключением может быть использование findвстроенной boshоболочки (POSIXified форка оболочки Bourne, разработанной @schily ), которая имеет -callпредикат для оценки кода в оболочке без необходимости создавать новый интерпретатор sh:

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

Или используйте perls 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' .

Дольше, но быстрее, чем zsh's' printf '%s\n' **/.git(:h)(который опускается во все не скрытые каталоги) или GNU find, find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -printкоторый запускает одну testкоманду в новом процессе для каждого не скрытого каталога.

Стефан Шазелас
источник
1
Обратите внимание, что это также .gitможет быть файл - черезgit worktree
Стивен Пенни
1
Спасибо @StevenPenny, я не знал об этом. Я теперь изменил -dс на -e.
Стефан Шазелас
1

Если вы используете locate, вы можете найти каталоги с:

locate .git | grep "/.git$"

Список результатов быстр, и дальнейшая обработка также проста.

Jarivaa
источник
2
locate '*/.git'должно быть достаточно.
Стефан Шазелас
0

использование

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

timeэто, чтобы увидеть разницу с и без -prune.

Это основано на решении в man find. Вы можете отредактировать CVSи, svnесли не требуется. Содержание man-страницы следует

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

Учитывая следующий каталог проектов и связанные с ними административные каталоги SCM, выполните эффективный поиск корней проектов:

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

В этом примере -pruneпредотвращается ненужный спуск в каталоги, которые уже были обнаружены (например, мы не ищем project3/src, потому что мы уже нашли project3/.svn), но обеспечивает обнаружение дочерних каталогов ( project2и project3).

quiet_penguin
источник