You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
4.5 KiB

#!/usr/bin/env bash
BASEDIR="$PWD"
ENVDIR="$PWD/env"
usage() {
cat <<EOF
Usage: $0 [options] <command> [arguments]
Commands:
help This help text
list List environments
clear Delete all environment and revert to flat config/files
new <name> Create a new environment
switch <name> Switch to a different environment
delete <name> Delete an environment
rename <newname> Rename the current environment
diff Show differences between current state and environment
save Save your changes to the environment
revert Revert your changes since last save
Options:
EOF
exit ${1:-1}
}
error() {
echo "$0: $*"
exit 1
}
ask_bool() {
local DEFAULT="$1"; shift
local def defstr val
case "$DEFAULT" in
1) def=0; defstr="Y/n";;
0) def=1; defstr="y/N";;
*) def=; defstr="y/n";;
esac
while [ -z "$val" ]; do
local VAL
echo -n "$* ($defstr): "
read VAL
case "$VAL" in
y*|Y*) val=0;;
n*|N*) val=1;;
*) val="$def";;
esac
done
return "$val"
}
env_init() {
local CREATE="$1"
if [ -z "$CREATE" ]; then
[ -d "$ENVDIR" ] || exit 0
fi
[ -x "$(which git 2>/dev/null)" ] || error "Git is not installed"
mkdir -p "$ENVDIR" || error "Failed to create the environment directory"
cd "$ENVDIR" || error "Failed to switch to the environment directory"
[ -d .git ] || {
git init &&
touch .config &&
mkdir files &&
git add . &&
git commit -q -m "Initial import"
} || {
rm -rf .git
error "Failed to initialize the environment directory"
}
}
env_sync_data() {
[ \! -L "$BASEDIR/.config" -a -f "$BASEDIR/.config" ] && mv "$BASEDIR/.config" "$ENVDIR"
git add .
git add -u
}
env_sync() {
local STR="$1"
env_sync_data
git commit -m "${STR:-Update} at $(date)"
}
env_link_config() {
rm -f "$BASEDIR/.config"
ln -s env/.config "$BASEDIR/.config"
mkdir -p "$ENVDIR/files"
[ -L "$BASEDIR/files" ] || ln -s env/files "$BASEDIR/files"
}
env_do_reset() {
git reset --hard HEAD
git clean -d -f
}
env_list() {
env_init
git branch | grep -vE '^. master$'
}
env_diff() {
env_init
env_sync_data
git diff --cached
env_link_config
}
env_save() {
env_init
env_sync
env_link_config
}
env_revert() {
env_init
env_do_reset
env_link_config
}
env_ask_sync() {
env_sync_data
LINES="$(env_diff | wc -l)" # implies env_init
[ "$LINES" -gt 0 ] && {
if ask_bool 1 "Do you want to save your changes"; then
env_sync
else
env_do_reset
fi
}
}
env_clear() {
env_init
[ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config"
[ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files"
[ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find | grep -vE '^\.$' > /dev/null )
env_sync_data
if ask_bool 1 "Do you want to keep your current config and files"; then
mkdir -p "$BASEDIR/files"
cp -a "$ENVDIR/files/*" "$BASEDIR/files" 2>/dev/null >/dev/null
cp "$ENVDIR/.config" "$BASEDIR/"
else
rm -rf "$BASEDIR/files" "$BASEDIR/.config"
fi
cd "$BASEDIR"
rm -rf "$ENVDIR"
}
env_delete() {
local name="${1##*/}"
env_init
[ -z "$name" ] && usage
branch="$(git branch | grep '^\* ' | awk '{print $2}')"
[ "$name" = "$branch" ] && error "cannot delete the currently selected environment"
git branch -D "$name"
}
env_switch() {
local name="${1##*/}"
[ -z "$name" ] && usage
env_init
env_ask_sync
git checkout "$name" || error "environment '$name' not found"
env_link_config
}
env_rename() {
local NAME="${1##*/}"
env_init
git branch -m "$NAME"
}
env_new() {
local NAME="$1"
local branch
local from="master"
[ -z "$NAME" ] && usage
env_init 1
branch="$(git branch | grep '^\* ' | awk '{print $2}')"
if [ -n "$branch" -a "$branch" != "master" ]; then
env_ask_sync
if ask_bool 0 "Do you want to clone the current environment?"; then
from="$branch"
fi
rm -f "$BASEDIR/.config" "$BASEDIR/files"
fi
git checkout -b "$1" "$from"
if [ -f "$BASEDIR/.config" -o -d "$BASEDIR/files" ]; then
if ask_bool 1 "Do you want to keep your current config and files?"; then
[ -d "$BASEDIR/files" -a \! -L "$BASEDIR/files" ] && {
mkdir -p "$ENVDIR/files"
mv "$BASEDIR/files/*" "$ENVDIR/files/" 2>/dev/null
rmdir "$BASEDIR/files"
}
env_sync
else
rm -rf "$BASEDIR/.config" "$BASEDIR/files"
fi
fi
env_link_config
}
COMMAND="$1"; shift
case "$COMMAND" in
help) usage 0;;
new) env_new "$@";;
list) env_list "$@";;
clear) env_clear "$@";;
switch) env_switch "$@";;
delete) env_delete "$@";;
rename) env_rename "$@";;
diff) env_diff "$@";;
save) env_save "$@";;
revert) env_revert "$@";;
*) usage;;
esac