Skip to content

Instantly share code, notes, and snippets.

@amtoine
Last active October 12, 2023 12:02
Show Gist options
  • Save amtoine/7e54c1f0d46f7da26726c6e1a4ca41b1 to your computer and use it in GitHub Desktop.
Save amtoine/7e54c1f0d46f7da26726c6e1a4ca41b1 to your computer and use it in GitHub Desktop.
List Git repos

💡 Note
the commands are not able to list bare repositories, because bare repositories do not have a .git/ directory, thus, the true_store needs to filter out the bare repositories

let true_store = $repos | where not bare | get path

💡 Note
the ls and glob Nushell commands do not have a --type directory option, so do not use it for fd and find

use the script

source main.nu
try ...

results on my machine and my quite big repo store

💡 Note
generated with

try --rounds 25 --use-true-store
   | update mean { format duration ms }
   | update std { format duration ms }
   | to md --pretty
name mean std
find 1207.19 ms 125.09 ms
fd 443.31 ms 40.51 ms
ls 3146.65 ms 102.85 ms
glob 1091.34 ms 3.89 ms

for 105 actual repos and 383858 files in total reported by ^find $env.GIT_REPOS_HOME

use std [assert, bench]
# create a store of fake repos
def create-store [store: path]: table<path: string, bare: bool> -> nothing {
let repos = $in
if ($store | path exists) {
rm --recursive --verbose --force $store
}
mkdir $store
for repo in $repos {
if $repo.bare {
git init --bare ($store | path join $repo.path)
} else {
git init ($store | path join $repo.path)
}
}
}
def try [--rounds: int = 50, --use-true-store]: nothing -> table<name: string, mean: duration: std: duration> {
let store = if $use_true_store {
$env.GIT_REPOS_HOME
} else {
$nu.temp-path | path join "nu-git-manager/tests/list-all-repos-in-store"
}
let repos = [
[path, bare];
["a/normal/", false],
["a/bare/", true],
["b/c/d/normal/", false],
["b/c/d/bare/", true],
["a/normal/b/nested", false],
]
if not $use_true_store {
$repos | create-store $store
}
let true_store = $repos | where not bare | get path
let cases = [
[name, code];
["find", {|| ^find $store -name '.git' -prune | lines }],
["fd", {|| ^fd '\.git$' --prune --unrestricted $store | lines }],
["ls", {|| ls ($store | path join "**/*/.git/") | get name }],
["glob", {|| glob ($store | path join "**/.git") }]
]
print --no-newline "checking equality between benches... "
let actuals = $cases | each { do $in.code | each { str replace --regex '/.git/?' ''} | sort }
if not $use_true_store {
let expected = $true_store
| each {|it| $store | path join $it | str trim --right --char (char path_sep)}
| sort
assert equal $actuals.0 $expected
}
for actual in ($actuals | range 1..) {
assert equal $actuals.0 $actual
}
print $"(ansi green)all good(ansi reset)"
$cases | each { |case|
print $"measuring ($case.name)..."
bench --verbose --rounds $rounds $case.code
| merge {name: $case.name}
| reject times
| move name --before mean
}
}

the two closures below will also list the bare repositories in $env.GIT_REPOS_HOME by looking for the HEAD file in all subdirectories, and then filtering out the invalid / duplicate locations:

let glob = { ||
    glob ($env.GIT_REPOS_HOME | path join "**/HEAD") --not [
        **/.git/**/refs/remotes/**/HEAD,
        **/.git/modules/**/HEAD,
        **/logs/HEAD
    ]
        | str replace --regex '/(.git/)?HEAD$' ''
}
let fd = { ||
    ^fd --glob '**/HEAD' --unrestricted $env.GIT_REPOS_HOME
        | lines
        | find --invert --regex .*/\.git/?.*/refs/remotes/.*/HEAD
        | find --invert --regex .*/\.git/modules/.*/HEAD
        | find --invert --regex .*/logs/HEAD
        | str replace --regex '/(.git/)?HEAD$' ''
}

one can see that they give the exact same answer :ok_hand

(do $glob | sort) == (do $fd | sort)

however, the $fd closure is quite a lot faster than $glob 🤔

use std bench
print $"glob: (bench --verbose --rounds 5 --pretty $glob)"
print $"fd: (bench --verbose --rounds 5 --pretty $fd)"

will print

glob: 1sec 33ms 311µs 494ns +/- 22ms 279µs 308ns
fd: 308ms 566µs 550ns +/- 18ms 89µs 678ns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment