|
@@ -0,0 +1,219 @@
|
|
|
+#!/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_save() {
|
|
|
+ env_init
|
|
|
+ env_sync
|
|
|
+ env_link_config
|
|
|
+}
|
|
|
+
|
|
|
+env_revert() {
|
|
|
+ env_init
|
|
|
+ env_do_reset
|
|
|
+ env_link_config
|
|
|
+}
|
|
|
+
|
|
|
+env_ask_sync() {
|
|
|
+ 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_sync_data
|
|
|
+ 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##*/}"
|
|
|
+ [ -z "$name" ] && usage
|
|
|
+ [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found"
|
|
|
+ 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
|
|
|
+ [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found"
|
|
|
+
|
|
|
+ env_init
|
|
|
+ env_ask_sync
|
|
|
+ git-checkout "$NAME"
|
|
|
+ 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" ] && {
|
|
|
+ mv "$BASEDIR/files/"* "$ENVDIR/" 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
|