Bagaimana menemukan semua repositori git di dalam folder yang diberikan (cepat)

9

Pendekatan naif adalah find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} , tetapi itu terlalu lambat bagi saya, karena saya memiliki banyak struktur folder yang dalam di dalam repositori git (setidaknya saya berpikir bahwa ini adalah alasannya). Saya telah membaca tentang hal itu yang dapat saya gunakan pruneuntuk mencegah penemuan berulang ke direktori setelah menemukan sesuatu, tetapi ada dua hal. Saya tidak yakin bagaimana ini bekerja (maksud saya saya tidak mengerti apa yang saya prunelakukan walaupun saya sudah membaca halaman manual) dan yang kedua itu tidak akan berfungsi dalam kasus saya, karena itu akan mencegah findpengulangan ke .gitfolder tetapi tidak ke semua folder lain.

Jadi yang sebenarnya saya butuhkan adalah:

untuk semua subdirektori, periksa apakah mereka berisi .gitfolder dan jika kemudian berhenti mencari di cabang sistem file ini dan laporkan hasilnya. Akan sempurna jika ini juga akan mengecualikan direktori tersembunyi dari pencarian.

pengguna1685095
sumber

Jawaban:

8

Oke, saya masih tidak yakin bagaimana ini bekerja, tapi saya sudah mengujinya dan berfungsi.

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

6 directories, 0 files

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

Saya menantikan untuk membuat hal yang sama lebih cepat.

pengguna1685095
sumber
2
Ini -prunecara ini: Anda mulai akar pohon Anda bergerak turun dan ketika kondisi tertentu berlaku Anda memotong dari seluruh subpohon (seperti nyata "pemangkasan"), sehingga Anda tidak akan melihat lagi node di sub ini .
phk
@ pk oh, terima kasih. Saya sepertinya mengerti sekarang. Kami mencari direktori -type duntuk kondisi mana test -e ...yang benar dan jika itu benar kami melakukan tindakan -print -pruneyang berarti mencetaknya dan memotong subtree, kan?
user1685095
Ya, kami memotong subtree yang merupakan root.
phk
Solusi cepat untuk menggunakan solusi Anda untuk "memperbarui" semua repositori git: find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU paralleladalah pengganti yang sangat berguna untukxargs
Marcello Romani
Anda tidak akan mendapatkan sub-modul, yang juga merupakan repositori git. Anda mungkin ingin mengambilnya dengan mengambil sub-modul secara rekursif, begitu daftar root-repos dikembalikan oleh perintah ini.
hoijui
2

Kemungkinan Solusi

Untuk GNU finddan implementasi lainnya yang mendukung -execdir:

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

(lihat komentar)

Hal-hal yang sebelumnya dibahas

Solusi jika pemangkasan di bawah .gitini sudah cukup

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

Jika -printf '%h'didukung (seperti halnya GNU find), kami tidak perlu dirname:

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

Setelah menemukan folder .gitdi jalur saat ini akan mengeluarkannya dan kemudian berhenti mencari lebih jauh ke bawah pohon.

Solusi jika seluruh pohon folder harus dipangkas setelah .gitditemukan

Menggunakan -quitjika Anda findmendukungnya:

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

(Menurut posting rinci oleh Stéphane Chazelas -quit ini didukung di GNU dan FreeBSD finddan di NetBSD sebagai -exit.)

Lagi dengan -printf '%h'jika didukung:

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

Solusi untuk pemangkasan pada tingkat yang sama dengan di mana .gitfolder tersebut berada

Lihat bagian "Kemungkinan Solusi" untuk solusi saat ini untuk masalah khusus ini.

(Oh dan jelas solusi menggunakan xargsanggap tidak ada baris baru di jalur, jika tidak, Anda akan memerlukan sihir null-byte.)

phk
sumber
jika dir1berisi dua direktori dirxdan dirymasing-masing berisi .gitdirektori, ini hanya melaporkan dirx/.git
iruvar
@iruvar Ah OK, saya salah paham dengan Anda dalam hal ini, saya akan mencoba untuk mengulang solusinya.
phk
masalah dengan solusi baru Anda adalah ini jika dir1/.gitada, itu masih turun dir1/dirx, yang, berdasarkan pembacaan saya tentang persyaratan OP, tidak diinginkan
iruvar
@iruvar OK, menambahkan itu juga. Adakah ide lain tentang apa arti OP? ;-)
phk
@iruvar tepatnya
user1685095
2

Idealnya, Anda ingin merayapi pohon direktori untuk direktori yang berisi .gitentri dan berhenti mencari lebih jauh ke bawah itu (dengan asumsi Anda tidak memiliki repo git lebih lanjut di dalam repo git).

Masalahnya adalah bahwa dengan standar find, melakukan pemeriksaan semacam ini (bahwa direktori berisi .gitentri) melibatkan pemijahan proses yang mengeksekusi testutilitas menggunakan -execpredikat, yang akan menjadi kurang efisien daripada daftar isi beberapa direktori.

Pengecualiannya adalah jika Anda menggunakan findbuiltin dari boshshell (garpu POSIXified dari Bourne shell yang dikembangkan oleh @schily ) yang memiliki -callpredikat untuk mengevaluasi kode dalam shell tanpa harus menelurkan juru bahasa baru:

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

Atau menggunakan 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' .

Lagi, tapi lebih cepat dari zsh's printf '%s\n' **/.git(:h)(yang turun ke semua direktori non-tersembunyi), atau GNU find' s find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -printyang berjalan satu testperintah dalam proses baru untuk setiap direktori non-tersembunyi.

Stéphane Chazelas
sumber
1
Catatan yang .gitdapat berupa file juga - viagit worktree
Steven Penny
1
Terima kasih @StevenPenny, saya tidak menyadarinya. Saya sekarang telah mengubah -ds ke -e.
Stéphane Chazelas
1

Jika Anda menggunakan loc, Anda dapat menemukan direktori dengan:

locate .git | grep "/.git$"

Daftar hasil cepat dan pemrosesan lebih lanjut juga mudah.

Jarivaa
sumber
2
locate '*/.git'Seharusnya cukup.
Stéphane Chazelas
0

Menggunakan

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

timeini, untuk melihat perbedaannya dengan dan tanpa -prune.

Ini didasarkan pada solusi dalam man find. Anda dapat mengedit CVSdan svnjika tidak diperlukan. isi halaman manual mengikuti

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

Dengan direktori proyek berikut dan direktori administratif SCM terkait, lakukan pencarian efisien untuk akar proyek:

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

Dalam contoh ini, -prunecegah keturunan yang tidak perlu ke direktori yang telah ditemukan (misalnya, kami tidak mencari project3/src, karena kami sudah menemukan project3/.svn), tetapi memastikan direktori saudara ( project2dan project3) ditemukan.

quiet_penguin
sumber