env 4.6 KB

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