env 4.7 KB

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