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. env_link_config
  94. }
  95. env_save() {
  96. env_init
  97. env_sync
  98. env_link_config
  99. }
  100. env_revert() {
  101. env_init
  102. env_do_reset
  103. env_link_config
  104. }
  105. env_ask_sync() {
  106. env_sync_data
  107. LINES="$(env_diff | wc -l)" # implies env_init
  108. [ "$LINES" -gt 0 ] && {
  109. if ask_bool 1 "Do you want to save your changes"; then
  110. env_sync
  111. else
  112. env_do_reset
  113. fi
  114. }
  115. }
  116. env_clear() {
  117. env_init
  118. [ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config"
  119. [ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files"
  120. [ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find | grep -vE '^\.$' > /dev/null )
  121. env_sync_data
  122. if ask_bool 1 "Do you want to keep your current config and files"; then
  123. mkdir -p "$BASEDIR/files"
  124. cp -a "$ENVDIR/files/*" "$BASEDIR/files" 2>/dev/null >/dev/null
  125. cp "$ENVDIR/.config" "$BASEDIR/"
  126. else
  127. rm -rf "$BASEDIR/files" "$BASEDIR/.config"
  128. fi
  129. cd "$BASEDIR"
  130. rm -rf "$ENVDIR"
  131. }
  132. env_delete() {
  133. local name="${1##*/}"
  134. env_init
  135. [ -z "$name" ] && usage
  136. branch="$(git branch | grep '^\* ' | awk '{print $2}')"
  137. [ "$name" = "$branch" ] && error "cannot delete the currently selected environment"
  138. git branch -D "$name"
  139. }
  140. env_switch() {
  141. local name="${1##*/}"
  142. [ -z "$name" ] && usage
  143. env_init
  144. env_ask_sync
  145. git checkout "$name" || error "environment '$name' not found"
  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