#!/bin/sh
set -eu
TYPES='
git
pijul
fossil
bitkeeper
darcs
mercurial
cvs
'
guess_type() {
if [ -e "$1"/.git ] || { [ -n "${GIT_DIR:-}" ] && [ -n "$GIT_WORK_TREE" ]; }; then
echo git
elif [ -e "$1"/.pijul ]; then
echo pijul
elif [ -e "$1"/.hg ]; then
echo mercurial
elif [ -e "$1"/.bk ]; then
echo bitkeeper
elif [ -e "$1"/_darcs ]; then
echo darcs
elif [ -e "$1"/CVS/ ]; then
echo cvs
elif [ -e "$1"/.fslckout ]; then
echo fossil
else
return 1
fi
}
git_fetch() {
git fetch --all "$@"
}
fossil_fetch() {
fossil pull "$@"
}
darcs_fetch() {
darcs fetch "$@"
}
mercurial_fetch() {
hg pull "$@"
}
cvs_fetch() {
timeout 300 cvs update "$@"
}
git_status() {
git status "$@"
}
fossil_status() {
fossil status "$@"
fossil extras "$@"
}
git_diff() {
git diff "$@"
}
fossil_diff() {
fossil diff --unified | diff-so-fancy | less -R
}
git_ps1() {
BRANCH_NAME="$(git rev-parse --abbrev-ref HEAD)"
OUT="$(git status --short --branch --porcelain)"
BRANCH_LINE="$(printf '%s\n' "$OUT" | head -n 1)"
DIFF_LINES="$(printf '%s\n' "$OUT" | tail -n +2)"
IS_AHEAD=false
IS_BEHIND=false
if printf '%s\n' "$BRANCH_LINE" | grep -q 'ahead'; then
IS_AHEAD=true
fi
if printf '%s\n' "$BRANCH_LINE" | grep -q 'behind'; then
IS_BEHIND=true
fi
LINE=''
if [ "$IS_AHEAD" = true ] && [ "$IS_BEHIND" = true ]; then
LINE="^^^ $BRANCH_NAME vvv"
elif [ "$IS_AHEAD" = true ]; then
LINE="^ $BRANCH_NAME ^"
elif [ "$IS_BEHIND" = true ]; then
LINE="v $BRANCH_NAME v"
else
LINE="$BRANCH_NAME"
fi
HAS_DIFF=false
HAS_UNTRACKED=false
if printf '%s\n' "$DIFF_LINES" | grep -q '^[A|D|M|R| ][M|D| ]'; then
HAS_DIFF=true
fi
if printf '%s\n' "$DIFF_LINES" | grep -Eq '^([?][?]| A)'; then
HAS_UNTRACKED=true
fi
if [ "$HAS_DIFF" = true ]; then
COLOR_FN=redb
LINE="{$LINE}"
elif [ "$IS_AHEAD" = true ] || [ "$IS_BEHIND" = true ]; then
COLOR_FN=bluei
LINE="[$LINE]"
elif [ "$HAS_UNTRACKED" = true ]; then
COLOR_FN=lightblue
LINE="{$LINE}"
else
COLOR_FN=green
LINE="($LINE)"
fi
color -c "$COLOR_FN" "$LINE"
BRANCH_COUNT="$(git branch --list | wc -l)"
if [ "$BRANCH_COUNT" -gt 1 ]; then
color -c lightblue "<$BRANCH_COUNT>"
fi
STASH_COUNT="$(git stash list | wc -l)"
if [ "$STASH_COUNT" != 0 ]; then
color -c red "*$STASH_COUNT"
fi
color -c blacki " - git/$(git rev-parse HEAD)"
}
pijul_ps1() {
LINE="$(pijul channel | awk '$1 == "*" && $0=$2')"
DIFF_LINES="$(pijul diff -u -s)"
HAS_DIFF=false
HAS_UNTRACKED=false
if printf '%s\n' "$DIFF_LINES" | grep -q '^[A|D|M|R| ][M|D| ]'; then
HAS_DIFF=true
fi
if printf '%s\n' "$DIFF_LINES" | grep -Eq '^([?][?]| A)'; then
HAS_UNTRACKED=true
fi
if [ "$HAS_DIFF" = true ]; then
COLOR_FN=redb
LINE="{$LINE}"
elif [ "$IS_AHEAD" = true ] || [ "$IS_BEHIND" = true ]; then
COLOR_FN=bluei
LINE="[$LINE]"
elif [ "$HAS_UNTRACKED" = true ]; then
COLOR_FN=lightblue
LINE="{$LINE}"
else
COLOR_FN=green
LINE="($LINE)"
fi
color -c "$COLOR_FN" "$LINE"
CHANNEL_COUNT="$(pijul channel | wc -l)"
if [ "$CHANNEL_COUNT" -gt 1 ]; then
color -c lightblue "<$CHANNEL_COUNT>"
fi
CHANGEID="$(pijul log --limit 1 --output-format json |
jq -r '.[0].hash')"
color -c blacki " - pijul/$CHANGEID"
}
bitkeeper_ps1() {
printf ''
}
cvs_ps1() {
printf ''
}
fossil_ps1() {
BRANCH_NAME="$(fossil branch current)"
if [ -n "$(fossil extras)" ]; then
HAS_UNTRACKED=1
fi
BRANCH_MARKER="$BRANCH_NAME"
if [ -n "${HAS_UNTRACKED:-}" ]; then
COLOR_FN=lightblue
LINE="($BRANCH_MARKER)"
else
COLOR_FN=green
LINE="($BRANCH_MARKER)"
fi
color -c "$COLOR_FN" "$LINE"
color -c blacki " - fossil/$(fossil info | awk '/^checkout:/ { print $2 }')"
}
mercurial_ps1() {
BRANCH_NAME="$(hg branch)"
}
git_gc() {
git remote prune origin "$@"
git gc
}
git_ls() {
git ls-files
}
pijul_ls() {
pijul ls
}
fossil_ls() {
fossil ls
}
git_clean() {
git clean -nffdx
}
fossil_clean() {
fossil clean -n -v -x --emptydirs
}
usage() {
cat <<-'EOF'
Usage:
vcs [-C DIR] ACTION
vcs [-C DIR] -t
vcs -l
vcs -h
EOF
}
help() {
cat <<-'EOF'
Options:
-C DIR change to DIR instead of PWD
-l list the supported VCS types
-t show the guesses VCS type
-h, --help show this message
ACTION the action to be performed on the repository:
- fetch
- ps1
- status
- gc
- ls
Wrapper command for normalizing options of differenct version
control software.
Examples:
Say the type of repo in "project/":
$ vcs -C project/ -t
Fetch updates for VCS in current directory:
$ vcs fetch
EOF
}
for flag in "$@"; do
case "$flag" in
(--)
break
;;
(--help)
usage
help
exit
;;
(*)
;;
esac
done
while getopts 'C:lth' flag; do
case "$flag" in
C)
cd "$OPTARG"
;;
(l)
# shellcheck disable=2086
echo $TYPES | tr ' ' '\n'
exit
;;
(t)
guess_type "$PWD" || {
printf 'Could not guess the type of the repository.\n' >&2
exit 2
}
exit
;;
(h)
usage
help
exit
;;
(*)
usage >&2
exit 2
esac
done
shift $((OPTIND - 1))
ACTION="${1:-}"
eval "$(assert-arg -- "$ACTION" 'ACTION')"
shift
if [ "${1:-}" = '--' ]; then
shift
fi
TYPE="$(guess_type "$PWD")"
if [ -z "$TYPE" ]; then
printf 'Could not guess the type of the repository.\n' >&2
exit 2
fi
CMD="$TYPE"_"$ACTION"
if ! command -v "$CMD" >/dev/null; then
printf 'Invalid ACTION: "%s".\n\n' "$ACTION" >&2
usage >&2
exit 2
fi
"$CMD" "$@"