env 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. env_init
  134. [ -z "$name" ] && usage
  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. env_init
  143. env_ask_sync
  144. git checkout "$name" || error "environment '$name' not found"
  145. env_link_config
  146. }
  147. env_rename() {
  148. local NAME="${1##*/}"
  149. env_init
  150. git branch -m "$NAME"
  151. }
  152. env_new() {
  153. local NAME="$1"
  154. local branch
  155. local from="master"
  156. [ -z "$NAME" ] && usage
  157. env_init 1
  158. branch="$(git branch | grep '^\* ' | awk '{print $2}')"
  159. if [ -n "$branch" -a "$branch" != "master" ]; then
  160. env_ask_sync
  161. if ask_bool 0 "Do you want to clone the current environment?"; then
  162. from="$branch"
  163. fi
  164. rm -f "$BASEDIR/.config" "$BASEDIR/files"
  165. fi
  166. git checkout -b "$1" "$from"
  167. if [ -f "$BASEDIR/.config" -o -d "$BASEDIR/files" ]; then
  168. if ask_bool 1 "Do you want to keep your current config and files?"; then
  169. [ -d "$BASEDIR/files" -a \! -L "$BASEDIR/files" ] && {
  170. mv "$BASEDIR/files/"* "$ENVDIR/" 2>/dev/null
  171. rmdir "$BASEDIR/files"
  172. }
  173. env_sync
  174. else
  175. rm -rf "$BASEDIR/.config" "$BASEDIR/files"
  176. fi
  177. fi
  178. env_link_config
  179. }
  180. COMMAND="$1"; shift
  181. case "$COMMAND" in
  182. help) usage 0;;
  183. new) env_new "$@";;
  184. list) env_list "$@";;
  185. clear) env_clear "$@";;
  186. switch) env_switch "$@";;
  187. delete) env_delete "$@";;
  188. rename) env_rename "$@";;
  189. diff) env_diff "$@";;
  190. save) env_save "$@";;
  191. revert) env_revert "$@";;
  192. *) usage;;
  193. esac