env 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #!/usr/bin/env bash
  2. BASEDIR="$PWD"
  3. ENVDIR="$PWD/env"
  4. usage() {
  5. cat <<EOF
  6. Usage: $0 [options] <command> [arguments]
  7. Commands:
  8. help This help text
  9. list List environments
  10. clear Delete all environment and revert to flat config/files
  11. new <name> Create a new environment
  12. switch <name> Switch to a different environment
  13. delete <name> Delete an environment
  14. rename <newname> Rename the current environment
  15. diff Show differences between current state and environment
  16. save Save your changes to the environment
  17. revert Revert your changes since last save
  18. Options:
  19. EOF
  20. exit ${1:-1}
  21. }
  22. error() {
  23. echo "$0: $*"
  24. exit 1
  25. }
  26. ask_bool() {
  27. local DEFAULT="$1"; shift
  28. local def defstr val
  29. case "$DEFAULT" in
  30. 1) def=0; defstr="Y/n";;
  31. 0) def=1; defstr="y/N";;
  32. *) def=; defstr="y/n";;
  33. esac
  34. while [ -z "$val" ]; do
  35. local VAL
  36. echo -n "$* ($defstr): "
  37. read VAL
  38. case "$VAL" in
  39. y*|Y*) val=0;;
  40. n*|N*) val=1;;
  41. *) val="$def";;
  42. esac
  43. done
  44. return "$val"
  45. }
  46. env_init() {
  47. local CREATE="$1"
  48. if [ -z "$CREATE" ]; then
  49. [ -d "$ENVDIR" ] || exit 0
  50. fi
  51. [ -x "$(which git 2>/dev/null)" ] || error "Git is not installed"
  52. mkdir -p "$ENVDIR" || error "Failed to create the environment directory"
  53. cd "$ENVDIR" || error "Failed to switch to the environment directory"
  54. [ -d .git ] || {
  55. git init &&
  56. touch .config &&
  57. mkdir files &&
  58. git-add . &&
  59. git-commit -q -m "Initial import"
  60. } || {
  61. rm -rf .git
  62. error "Failed to initialize the environment directory"
  63. }
  64. }
  65. env_sync_data() {
  66. [ \! -L "$BASEDIR/.config" -a -f "$BASEDIR/.config" ] && mv "$BASEDIR/.config" "$ENVDIR"
  67. git-add .
  68. git-add -u
  69. }
  70. env_sync() {
  71. local STR="$1"
  72. env_sync_data
  73. git-commit -m "${STR:-Update} at $(date)"
  74. }
  75. env_link_config() {
  76. rm -f "$BASEDIR/.config"
  77. ln -s env/.config "$BASEDIR/.config"
  78. mkdir -p "$ENVDIR/files"
  79. [ -L "$BASEDIR/files" ] || ln -s env/files "$BASEDIR/files"
  80. }
  81. env_do_reset() {
  82. git-reset --hard HEAD
  83. git-clean -d -f
  84. }
  85. env_list() {
  86. env_init
  87. git-branch | grep -vE '^. master$'
  88. }
  89. env_diff() {
  90. env_init
  91. env_sync_data
  92. git-diff --cached
  93. }
  94. env_save() {
  95. env_init
  96. env_sync
  97. env_link_config
  98. }
  99. env_revert() {
  100. env_init
  101. env_do_reset
  102. env_link_config
  103. }
  104. env_ask_sync() {
  105. LINES="$(env_diff | wc -l)" # implies env_init
  106. [ "$LINES" -gt 0 ] && {
  107. if ask_bool 1 "Do you want to save your changes"; then
  108. env_sync
  109. else
  110. env_sync_data
  111. env_do_reset
  112. fi
  113. }
  114. }
  115. env_clear() {
  116. env_init
  117. [ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config"
  118. [ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files"
  119. [ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find | grep -vE '^\.$' > /dev/null )
  120. env_sync_data
  121. if ask_bool 1 "Do you want to keep your current config and files"; then
  122. mkdir -p "$BASEDIR/files"
  123. cp -a "$ENVDIR/files/*" "$BASEDIR/files" 2>/dev/null >/dev/null
  124. cp "$ENVDIR/.config" "$BASEDIR/"
  125. else
  126. rm -rf "$BASEDIR/files" "$BASEDIR/.config"
  127. fi
  128. cd "$BASEDIR"
  129. rm -rf "$ENVDIR"
  130. }
  131. env_delete() {
  132. local name="${1##*/}"
  133. [ -z "$name" ] && usage
  134. [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found"
  135. branch="$(git-branch | grep '^\* ' | awk '{print $2}')"
  136. [ "$name" = "branch" ] && error "cannot delete the currently selected environment"
  137. git-branch -D "$name"
  138. }
  139. env_switch() {
  140. local name="${1##*/}"
  141. [ -z "$name" ] && usage
  142. [ -f "$envdir/.git/refs/heads/$name" ] || error "environment '$name' not found"
  143. env_init
  144. env_ask_sync
  145. git-checkout "$NAME"
  146. env_link_config
  147. }
  148. env_rename() {
  149. local NAME="${1##*/}"
  150. env_init
  151. git-branch -m "$NAME"
  152. }
  153. env_new() {
  154. local NAME="$1"
  155. local branch
  156. local from="master"
  157. [ -z "$NAME" ] && usage
  158. env_init 1
  159. branch="$(git-branch | grep '^\* ' | awk '{print $2}')"
  160. if [ -n "$branch" -a "$branch" != "master" ]; then
  161. env_ask_sync
  162. if ask_bool 0 "Do you want to clone the current environment?"; then
  163. from="$branch"
  164. fi
  165. rm -f "$BASEDIR/.config" "$BASEDIR/files"
  166. fi
  167. git-checkout -b "$1" "$from"
  168. if [ -f "$BASEDIR/.config" -o -d "$BASEDIR/files" ]; then
  169. if ask_bool 1 "Do you want to keep your current config and files?"; then
  170. [ -d "$BASEDIR/files" -a \! -L "$BASEDIR/files" ] && {
  171. mv "$BASEDIR/files/"* "$ENVDIR/" 2>/dev/null
  172. rmdir "$BASEDIR/files"
  173. }
  174. env_sync
  175. else
  176. rm -rf "$BASEDIR/.config" "$BASEDIR/files"
  177. fi
  178. fi
  179. env_link_config
  180. }
  181. COMMAND="$1"; shift
  182. case "$COMMAND" in
  183. help) usage 0;;
  184. new) env_new "$@";;
  185. list) env_list "$@";;
  186. clear) env_clear "$@";;
  187. switch) env_switch "$@";;
  188. delete) env_delete "$@";;
  189. rename) env_rename "$@";;
  190. diff) env_diff "$@";;
  191. save) env_save "$@";;
  192. revert) env_revert "$@";;
  193. *) usage;;
  194. esac