Speed up project.sh ~21x, 0.703s -> 0.033s
This commit is contained in:
54
project.sh
54
project.sh
@@ -6,39 +6,45 @@ projects_dir=$HOME/Projects
|
|||||||
|
|
||||||
# Print the sorted, de-duplicated list of selectable projects.
|
# Print the sorted, de-duplicated list of selectable projects.
|
||||||
list_projects() {
|
list_projects() {
|
||||||
local projects=() dir relative git_dirs kept_roots=() root kept
|
local projects=() dir relative clone worktree projects_real
|
||||||
|
|
||||||
|
# git reports symlink-resolved paths, which need not spell $projects_dir the
|
||||||
|
# way we do, so keep the resolved form around to match them against too.
|
||||||
|
projects_real=$(cd "$projects_dir" 2>/dev/null && pwd -P) ||
|
||||||
|
projects_real=$projects_dir
|
||||||
|
|
||||||
# All depth-2 directories without @ are included unconditionally
|
|
||||||
for dir in "$projects_dir"/*/*/; do
|
for dir in "$projects_dir"/*/*/; do
|
||||||
[ -d "$dir" ] || continue
|
[ -d "$dir" ] || continue
|
||||||
relative="${dir#$projects_dir/}"
|
relative="${dir#$projects_dir/}"
|
||||||
relative="${relative%/}"
|
relative="${relative%/}"
|
||||||
[[ "$relative" != *@* ]] && projects+=("$relative")
|
|
||||||
done
|
|
||||||
|
|
||||||
# For @ directories, only show the top-most directory that holds a .git
|
# All depth-2 directories without @ are included unconditionally
|
||||||
# entry. Sorting makes every parent precede its descendants, so once a root
|
if [[ "$relative" != *@* ]]; then
|
||||||
# is kept we drop anything nested beneath it — typically a git submodule —
|
projects+=("$relative")
|
||||||
# while separate clones and worktrees, each in their own directory, survive.
|
continue
|
||||||
if command -v fd &>/dev/null; then
|
|
||||||
git_dirs=$(fd -H '^\.git$' "$projects_dir" || true)
|
|
||||||
else
|
|
||||||
git_dirs=$(find "$projects_dir" \( -name 'node_modules' -o -name '.git' \) \
|
|
||||||
-prune -name '.git' -print || true)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while IFS= read -r root; do
|
# A `repo@branch` directory is the parent of the worktrees hanging off the
|
||||||
for kept in "${kept_roots[@]}"; do
|
# `repo` clone, nested one level deeper per `/` in the branch name. Ask
|
||||||
if [[ "$root" == "$kept/"* ]]; then
|
# that clone which worktrees it has instead of scanning: git already knows,
|
||||||
continue 2
|
# whereas a scan deep enough to reach them has to walk every working tree
|
||||||
fi
|
# under $projects_dir to find a handful of entries. Submodules aren't
|
||||||
|
# worktrees, so they never turn up and need no filtering out. `git worktree
|
||||||
|
# list` also names the clone itself, which the branch above already added —
|
||||||
|
# the final sort -u collapses the pair.
|
||||||
|
clone="$projects_dir/${relative%%@*}"
|
||||||
|
while IFS= read -r worktree; do
|
||||||
|
case $worktree in
|
||||||
|
"$projects_dir"/*) worktree=${worktree#"$projects_dir"/} ;;
|
||||||
|
"$projects_real"/*) worktree=${worktree#"$projects_real"/} ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
# Registered but deleted worktrees linger until `git worktree prune`.
|
||||||
|
[ -d "$projects_dir/$worktree" ] || continue
|
||||||
|
projects+=("$worktree")
|
||||||
|
done < <(git -C "$clone" worktree list --porcelain 2>/dev/null |
|
||||||
|
sed -n 's|^worktree ||p')
|
||||||
done
|
done
|
||||||
kept_roots+=("$root")
|
|
||||||
projects+=("$root")
|
|
||||||
done < <(printf '%s\n' "$git_dirs" |
|
|
||||||
grep '@' |
|
|
||||||
sed -E -e "s|^$projects_dir/||" -e 's|/\.git/?$||' |
|
|
||||||
sort -u)
|
|
||||||
|
|
||||||
printf '%s\n' "${projects[@]}" | sort -u
|
printf '%s\n' "${projects[@]}" | sort -u
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user