diff options
author | EuAndreh <eu@euandre.org> | 2022-10-21 00:24:53 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2022-10-21 00:24:55 -0300 |
commit | 8d9df7203a9bc81114a63ec9294b73487edfde79 (patch) | |
tree | c43f3f1123e888492dd03dfa6ccbf45fc2f0f9ce /bin/repos | |
parent | Makefile: Exclude global gitignore file from check-fixme target (diff) | |
download | dotfiles-8d9df7203a9bc81114a63ec9294b73487edfde79.tar.gz dotfiles-8d9df7203a9bc81114a63ec9294b73487edfde79.tar.xz |
bin/repos: Add new working utility
This will be the base replacement for mr(1) (A.K.A. myrepos). Instead
of having to feed what repositories to track or not manually to the
~/.mrconfig file, dynamically discover them with this new utility.
Diffstat (limited to 'bin/repos')
-rwxr-xr-x | bin/repos | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/bin/repos b/bin/repos new file mode 100755 index 0000000..27e0879 --- /dev/null +++ b/bin/repos @@ -0,0 +1,187 @@ +#!/bin/sh +set -eu + + +usage() { + cat <<-'EOF' + Usage: + repos [-v] [DIRECTORY] + repos -h + EOF +} + +help() { + cat <<-'EOF' + + + Options: + -v enable verbose mode + -h, --help show this message + + DIRECTORY the folder to traverse + + + Traverse DIRECTORY looking for VCS repositores. As soon as + one is found, stop recursing, and emit its name alongside its + type. + + On verbose mode, print the directories being visited. + + + Examples: + + Show all repositories under ~/dev/, excluding a few directories: + + $ repos -e ~/dev/go/ -e ~/dev/quicklisp/ ~/dev/ + # ... + EOF +} + + +for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac +done + + +# Similar to urlencode but only for % and \n (and not \0). +array_encode() { + sed 's/%/%25/g' | sed -e :a -e '$!N; s/\n/%0A/; ta' +} + +array_decode() { + sed -e 's/%0A/\ +/g' -e 's/%25/%/g' +} + + +# +# To avoid needing to keep decoding the array elements on every call to +# `arr_includes`, assume directory names don't contain newlines. This makes the +# current code scanning ~/dev/ from 8 seconds go to 1 second. +# + +arr_push() { + ARR="$1" + ELT="$2" + if [ -n "$ARR" ]; then + echo "$ARR" + fi + echo "$ELT" # | array_encode +} + +arr_includes() { + ARR="$1" + ELT="$2" + echo "$ARR" | while read -r el; do + # if [ "$(printf '%s\n' "$el" | array_decode)" = "$ELT" ]; then + if [ "$el" = "$ELT" ]; then + return 2 + fi + done + if [ $? = 2 ]; then + return 0 + else + return 1 + fi +} + +EXCLUDE= +VERBOSE=false +while getopts 'e:vh' flag; do + case "$flag" in + e) + case "$OPTARG" in + */) + ARG="$OPTARG" + ;; + *) + ARG="$OPTARG/" + ;; + esac + EXCLUDE="$(arr_push "$EXCLUDE" "$ARG")" + ;; + v) + VERBOSE=true + ;; + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + + +is_repository() { + if [ -e "$1"/.git ]; then + printf 'git:%s\n' "$1" + return 0 + elif [ -e "$1"/.hg ]; then + printf 'mercurial:%s\n' "$1" + return 0 + elif [ -e "$1"/.bk ]; then + printf 'bitkeeper:%s\n' "$1" + return 0 + elif [ -e "$1"/_darcs ]; then + printf 'darcs:%s\n' "$1" + return 0 + elif [ -e "$1"/CVS/ ]; then + printf 'cvs:%s\n' "$1" + return 0 + elif [ -e "$1"/"$(basename "$1")".fossil ]; then + printf 'fossil:%s\n' "$1" + return 0 + fi + return 1 +} + + +traverse_directory() { + if [ "$VERBOSE" = true ]; then + printf 'cur: %s\n' "$1" >&2 + fi + if arr_includes "$EXCLUDE" "$1"; then + return + fi + if is_repository "$1"; then + return + fi + for d in "$1"/*; do + if [ "$VERBOSE" = true ]; then + printf 'cur: %s\n' "$d" >&2 + fi + if [ ! -d "$d" ]; then + continue + fi + if arr_includes "$EXCLUDE" "$d/"; then + continue + fi + if ! is_repository "$d"; then + traverse_directory "$d" + fi + done +} + +if [ -z "${1:-}" ]; then + set -- "$PWD" +fi + +for dir in "$@"; do + traverse_directory "${dir%%/}" +done |