gitprompt.sh 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. #!/usr/bin/env bash
  2. function async_run() {
  3. {
  4. eval "$@" &> /dev/null
  5. }&
  6. }
  7. function git_prompt_dir() {
  8. # assume the gitstatus.sh is in the same directory as this script
  9. # code thanks to http://stackoverflow.com/questions/59895
  10. if [[ -z "${__GIT_PROMPT_DIR:+x}" ]]; then
  11. local SOURCE="${BASH_SOURCE[0]}"
  12. while [[ -h "${SOURCE}" ]]; do
  13. local DIR="$( command cd -P "$( dirname "${SOURCE}" )" && pwd )"
  14. SOURCE="$(readlink "${SOURCE}")"
  15. [[ ${SOURCE} != /* ]] && SOURCE="${DIR}/${SOURCE}"
  16. done
  17. __GIT_PROMPT_DIR="$( command cd -P "$( dirname "${SOURCE}" )" && pwd )"
  18. fi
  19. }
  20. function echoc() {
  21. echo -e "${1}${2}${ResetColor}" | sed 's/\\\]//g' | sed 's/\\\[//g'
  22. }
  23. function get_theme() {
  24. local CUSTOM_THEME_FILE="${HOME}/.git-prompt-colors.sh"
  25. if [[ ! (-z "${GIT_PROMPT_THEME_FILE:+x}" ) ]]; then
  26. CUSTOM_THEME_FILE="${GIT_PROMPT_THEME_FILE}"
  27. fi
  28. local DEFAULT_THEME_FILE="${__GIT_PROMPT_DIR}/themes/Default.bgptheme"
  29. if [[ -z "${GIT_PROMPT_THEME+x}" ]]; then
  30. if [[ -r "${CUSTOM_THEME_FILE}" ]]; then
  31. GIT_PROMPT_THEME="Custom"
  32. __GIT_PROMPT_THEME_FILE="${CUSTOM_THEME_FILE}"
  33. else
  34. GIT_PROMPT_THEME="Default"
  35. __GIT_PROMPT_THEME_FILE="${DEFAULT_THEME_FILE}"
  36. fi
  37. else
  38. if [[ "${GIT_PROMPT_THEME}" = "Custom" ]]; then
  39. GIT_PROMPT_THEME="Custom"
  40. __GIT_PROMPT_THEME_FILE="${CUSTOM_THEME_FILE}"
  41. if [[ ! (-r "${__GIT_PROMPT_THEME_FILE}") ]]; then
  42. GIT_PROMPT_THEME="Default"
  43. __GIT_PROMPT_THEME_FILE="${DEFAULT_THEME_FILE}"
  44. fi
  45. else
  46. local theme=""
  47. # use default theme, if theme was not found
  48. for themefile in "${__GIT_PROMPT_DIR}/themes/"*.bgptheme; do
  49. local basename=${themefile##*/}
  50. if [[ "${basename%.bgptheme}" = "${GIT_PROMPT_THEME}" ]]; then
  51. theme="${GIT_PROMPT_THEME}"
  52. break
  53. fi
  54. done
  55. if [[ "${theme}" = "" ]]; then
  56. GIT_PROMPT_THEME="Default"
  57. fi
  58. __GIT_PROMPT_THEME_FILE="${__GIT_PROMPT_DIR}/themes/${GIT_PROMPT_THEME}.bgptheme"
  59. fi
  60. fi
  61. }
  62. function git_prompt_load_colors() {
  63. if gp_set_file_var __PROMPT_COLORS_FILE prompt-colors.sh ; then
  64. # outsource the color defs
  65. source "${__PROMPT_COLORS_FILE}"
  66. else
  67. echo 1>&2 "Cannot find prompt-colors.sh!"
  68. fi
  69. }
  70. function git_prompt_load_theme() {
  71. get_theme
  72. local DEFAULT_THEME_FILE="${__GIT_PROMPT_DIR}/themes/Default.bgptheme"
  73. source "${DEFAULT_THEME_FILE}"
  74. source "${__GIT_PROMPT_THEME_FILE}"
  75. }
  76. function git_prompt_list_themes() {
  77. git_prompt_load_colors
  78. git_prompt_dir
  79. get_theme
  80. for themefile in "${__GIT_PROMPT_DIR}/themes/"*.bgptheme; do
  81. local basename="${themefile##*/}"
  82. local theme="${basename%.bgptheme}"
  83. if [[ "${GIT_PROMPT_THEME}" = "${theme}" ]]; then
  84. echoc "${Red}" "*${theme}"
  85. else
  86. echo "${theme}"
  87. fi
  88. done
  89. if [[ "${GIT_PROMPT_THEME}" = "Custom" ]]; then
  90. echoc "${Magenta}" "*Custom"
  91. else
  92. echoc "${Blue}" "Custom"
  93. fi
  94. }
  95. function git_prompt_make_custom_theme() {
  96. if [[ -r "${HOME}/.git-prompt-colors.sh" ]]; then
  97. echoc "${Red}" "You have already created a custom theme!"
  98. else
  99. git_prompt_dir
  100. local base="Default"
  101. if [[ -n "${1}" && -r "${__GIT_PROMPT_DIR}/themes/${1}.bgptheme" ]]; then
  102. base="${1}"
  103. echoc "${Green}" "Using theme ${Magenta}\"${base}\"${Green} as base theme!"
  104. else
  105. echoc "${Green}" "Using theme ${Magenta}\"Default\"${Green} as base theme!"
  106. fi
  107. if [[ "${base}" = "Custom" ]]; then
  108. echoc "${Red}" "You cannot use the custom theme as base"
  109. else
  110. echoc "${Green}" "Creating new custom theme in \"${HOME}/.git-prompt-colors.sh\""
  111. echoc "${DimYellow}" "Please add ${Magenta}\"GIT_PROMPT_THEME=Custom\"${DimYellow} to your .bashrc to use this theme"
  112. if [[ "${base}" == "Default" ]]; then
  113. cp "${__GIT_PROMPT_DIR}/themes/Custom.bgptemplate" "${HOME}/.git-prompt-colors.sh"
  114. else
  115. cp "${__GIT_PROMPT_DIR}/themes/${base}.bgptheme" "${HOME}/.git-prompt-colors.sh"
  116. fi
  117. fi
  118. fi
  119. }
  120. # gp_set_file_var ENVAR SOMEFILE
  121. #
  122. # If ENVAR is set, check that it's value exists as a readable file. Otherwise,
  123. # Set ENVAR to the path to SOMEFILE, based on $HOME, $__GIT_PROMPT_DIR, and the
  124. # directory of the current script. The SOMEFILE can be prefixed with '.', or
  125. # not.
  126. #
  127. # Return 0 (success) if ENVAR not already defined, 1 (failure) otherwise.
  128. function gp_set_file_var() {
  129. local envar="${1}"
  130. local file="${2}"
  131. if eval "[[ -n \"\${${envar}+x}\" && -r \"\${${envar}+x}\" ]]" ; then # is envar set to a readable file?
  132. local basefile
  133. eval "basefile=\"\`basename \\\"\${${envar}}\\\"\`\"" # assign basefile
  134. if [[ "${basefile}" = "${file}" || "${basefile}" = ".${file}" ]]; then
  135. return 0
  136. fi
  137. else # envar is not set, or it's set to a different file than requested
  138. eval "${envar}=" # set empty envar
  139. gp_maybe_set_envar_to_path "${envar}" "${HOME}/.${file}" "${HOME}/${file}" "${HOME}/lib/${file}" && return 0
  140. git_prompt_dir
  141. gp_maybe_set_envar_to_path "${envar}" "${__GIT_PROMPT_DIR}/${file}" "${0##*/}/${file}" && return 0
  142. fi
  143. return 1
  144. }
  145. # gp_maybe_set_envar_to_path ENVAR FILEPATH ...
  146. #
  147. # return 0 (true) if any FILEPATH is readable, set ENVAR to it
  148. # return 1 (false) if not
  149. function gp_maybe_set_envar_to_path() {
  150. local envar="${1}"
  151. shift
  152. local file
  153. for file in "${@}" ; do
  154. if [[ -r "${file}" ]]; then
  155. eval "${envar}=\"${file}\""
  156. return 0
  157. fi
  158. done
  159. return 1
  160. }
  161. # git_prompt_reset
  162. #
  163. # unsets selected GIT_PROMPT variables, causing the next prompt callback to
  164. # recalculate them from scratch.
  165. git_prompt_reset() {
  166. local var
  167. for var in GIT_PROMPT_DIR __GIT_PROMPT_COLORS_FILE __PROMPT_COLORS_FILE __GIT_STATUS_CMD GIT_PROMPT_THEME_NAME; do
  168. unset ${var}
  169. done
  170. }
  171. # gp_format_exit_status RETVAL
  172. #
  173. # echos the symbolic signal name represented by RETVAL if the process was
  174. # signalled, otherwise echos the original value of RETVAL
  175. gp_format_exit_status() {
  176. local RETVAL="${1}"
  177. local SIGNAL
  178. # Suppress STDERR in case RETVAL is not an integer (in such cases, RETVAL
  179. # is echoed verbatim)
  180. if [[ "${RETVAL}" -gt 128 ]] 2>/dev/null; then
  181. SIGNAL=$(( RETVAL - 128 ))
  182. kill -l "${SIGNAL}" 2>/dev/null || echo "${RETVAL}"
  183. else
  184. echo "${RETVAL}"
  185. fi
  186. }
  187. gp_format_username_repo() {
  188. git config --get remote.origin.url | sed 's|^.*//||; s/.*@//; s/[^:/]\+[:/]//; s/.git$//'
  189. }
  190. function git_prompt_config() {
  191. #Checking if root to change output
  192. _isroot=false
  193. [[ "${UID}" -eq 0 ]] && _isroot=true
  194. # There are two files related to colors:
  195. #
  196. # prompt-colors.sh -- sets generic color names suitable for bash 'PS1' prompt
  197. # git-prompt-colors.sh -- sets the GIT_PROMPT color scheme, using names from prompt-colors.sh
  198. git_prompt_load_colors
  199. # source the user's ~/.git-prompt-colors.sh file, or the one that should be
  200. # sitting in the same directory as this script
  201. git_prompt_load_theme
  202. if is_function prompt_callback; then
  203. prompt_callback="prompt_callback"
  204. else
  205. prompt_callback="prompt_callback_default"
  206. fi
  207. if [[ "${GIT_PROMPT_LAST_COMMAND_STATE:-0}" = 0 ]]; then
  208. LAST_COMMAND_INDICATOR="${GIT_PROMPT_COMMAND_OK}";
  209. else
  210. LAST_COMMAND_INDICATOR="${GIT_PROMPT_COMMAND_FAIL}";
  211. fi
  212. # replace _LAST_COMMAND_STATE_ token with the actual state
  213. GIT_PROMPT_LAST_COMMAND_STATE=$(gp_format_exit_status "${GIT_PROMPT_LAST_COMMAND_STATE}")
  214. LAST_COMMAND_INDICATOR="${LAST_COMMAND_INDICATOR//_LAST_COMMAND_STATE_/${GIT_PROMPT_LAST_COMMAND_STATE}}"
  215. # Do this only once to define PROMPT_START and PROMPT_END
  216. if [[ -z "${PROMPT_START:+x}" || -z "${PROMPT_END:+x}" ]]; then
  217. if [[ -z "${GIT_PROMPT_START:+x}" ]] ; then
  218. if ${_isroot}; then
  219. PROMPT_START="${GIT_PROMPT_START_ROOT-}"
  220. else
  221. PROMPT_START="${GIT_PROMPT_START_USER-}"
  222. fi
  223. else
  224. PROMPT_START="${GIT_PROMPT_START-}"
  225. fi
  226. if [[ -z "${GIT_PROMPT_END:+x}" ]] ; then
  227. if $_isroot; then
  228. PROMPT_END="${GIT_PROMPT_END_ROOT-}"
  229. else
  230. PROMPT_END="${GIT_PROMPT_END_USER-}"
  231. fi
  232. else
  233. PROMPT_END="${GIT_PROMPT_END-}"
  234. fi
  235. fi
  236. # set GIT_PROMPT_LEADING_SPACE to 0 if you want to have no leading space in front of the GIT prompt
  237. if [[ "${GIT_PROMPT_LEADING_SPACE:-1}" = "0" ]]; then
  238. PROMPT_LEADING_SPACE=""
  239. else
  240. PROMPT_LEADING_SPACE=" "
  241. fi
  242. if [[ "${GIT_PROMPT_ONLY_IN_REPO:-0}" == 1 ]]; then
  243. EMPTY_PROMPT="${OLD_GITPROMPT}"
  244. elif [[ "${GIT_PROMPT_WITH_VIRTUAL_ENV:-1}" == 1 ]]; then
  245. local ps="$(gp_add_virtualenv_to_prompt)${PROMPT_START}$(${prompt_callback})${PROMPT_END}"
  246. EMPTY_PROMPT="${ps//_LAST_COMMAND_INDICATOR_/${LAST_COMMAND_INDICATOR}}"
  247. else
  248. local ps="${PROMPT_START}$(${prompt_callback})${PROMPT_END}"
  249. EMPTY_PROMPT="${ps//_LAST_COMMAND_INDICATOR_/${LAST_COMMAND_INDICATOR}}"
  250. fi
  251. # fetch remote revisions every other $GIT_PROMPT_FETCH_TIMEOUT (default 5) minutes
  252. if [[ -z "${GIT_PROMPT_FETCH_TIMEOUT:+x}" ]]; then
  253. GIT_PROMPT_FETCH_TIMEOUT="5"
  254. fi
  255. if [[ -z "${__GIT_STATUS_CMD:+x}" ]] ; then # if GIT_STATUS_CMD not defined..
  256. git_prompt_dir
  257. if ! gp_maybe_set_envar_to_path __GIT_STATUS_CMD "${__GIT_PROMPT_DIR}/${GIT_PROMPT_STATUS_COMMAND}" ; then
  258. echo 1>&2 "Cannot find ${GIT_PROMPT_STATUS_COMMAND}!"
  259. fi
  260. # __GIT_STATUS_CMD defined
  261. fi
  262. unset GIT_BRANCH
  263. }
  264. function setLastCommandState() {
  265. GIT_PROMPT_LAST_COMMAND_STATE="${?}"
  266. }
  267. function we_are_on_repo() {
  268. if [[ -e "$(git rev-parse --git-dir 2> /dev/null)" ]]; then
  269. echo 1
  270. else
  271. echo 0
  272. fi
  273. }
  274. function update_old_git_prompt() {
  275. local in_repo=$(we_are_on_repo)
  276. if [[ "${GIT_PROMPT_OLD_DIR_WAS_GIT}" = 0 ]]; then
  277. OLD_GITPROMPT="${PS1}"
  278. fi
  279. GIT_PROMPT_OLD_DIR_WAS_GIT="${in_repo}"
  280. }
  281. function setGitPrompt() {
  282. update_old_git_prompt
  283. local repo=$(git rev-parse --show-toplevel 2> /dev/null)
  284. if [[ ! -e "${repo}" ]] && [[ "${GIT_PROMPT_ONLY_IN_REPO-}" = 1 ]]; then
  285. # we do not permit bash-git-prompt outside git repos, so nothing to do
  286. PS1="${OLD_GITPROMPT}"
  287. return
  288. fi
  289. local EMPTY_PROMPT
  290. local __GIT_STATUS_CMD
  291. git_prompt_config
  292. if [[ ! -e "${repo}" ]] || [[ "${GIT_PROMPT_DISABLE-}" = 1 ]]; then
  293. PS1="${EMPTY_PROMPT}"
  294. return
  295. fi
  296. local FETCH_REMOTE_STATUS=1
  297. if [[ "${GIT_PROMPT_FETCH_REMOTE_STATUS}" = 0 ]]; then
  298. FETCH_REMOTE_STATUS=0
  299. fi
  300. unset GIT_PROMPT_IGNORE
  301. OLD_GIT_PROMPT_SHOW_UNTRACKED_FILES="${GIT_PROMPT_SHOW_UNTRACKED_FILES}"
  302. unset GIT_PROMPT_SHOW_UNTRACKED_FILES
  303. OLD_GIT_PROMPT_IGNORE_SUBMODULES="${GIT_PROMPT_IGNORE_SUBMODULES}"
  304. unset GIT_PROMPT_IGNORE_SUBMODULES
  305. if [[ -e "${repo}/.bash-git-rc" ]]; then
  306. # The config file can only contain variable declarations on the form A_B=0 or G_P=all
  307. local CONFIG_SYNTAX="^(FETCH_REMOTE_STATUS|GIT_PROMPT_SHOW_UNTRACKED_FILES|GIT_PROMPT_IGNORE_SUBMODULES|GIT_PROMPT_IGNORE)=[0-9a-z]+$"
  308. if grep -q -v -E "${CONFIG_SYNTAX}" "${repo}/.bash-git-rc"; then
  309. echo ".bash-git-rc can only contain variable values on the form NAME=value. Ignoring file." >&2
  310. else
  311. source "${repo}/.bash-git-rc"
  312. fi
  313. fi
  314. if [[ -z "${GIT_PROMPT_SHOW_UNTRACKED_FILES+x}" ]]; then
  315. GIT_PROMPT_SHOW_UNTRACKED_FILES="${OLD_GIT_PROMPT_SHOW_UNTRACKED_FILES}"
  316. fi
  317. unset OLD_GIT_PROMPT_SHOW_UNTRACKED_FILES
  318. if [[ -z "${GIT_PROMPT_IGNORE_SUBMODULES+x}" ]]; then
  319. GIT_PROMPT_IGNORE_SUBMODULES="${OLD_GIT_PROMPT_IGNORE_SUBMODULES}"
  320. fi
  321. unset OLD_GIT_PROMPT_IGNORE_SUBMODULES
  322. if [[ "${GIT_PROMPT_IGNORE-}" = 1 ]]; then
  323. PS1="${EMPTY_PROMPT}"
  324. return
  325. fi
  326. if [[ "${FETCH_REMOTE_STATUS}" = 1 ]]; then
  327. checkUpstream
  328. fi
  329. updatePrompt
  330. }
  331. # some versions of find do not have -mmin
  332. _have_find_mmin=1
  333. function olderThanMinutes() {
  334. local matches
  335. local find_exit_code
  336. if [[ -z "${_find_command+x}" ]]; then
  337. if command -v gfind > /dev/null; then
  338. _find_command="gfind"
  339. else
  340. _find_command="find"
  341. fi
  342. fi
  343. if [[ "${_have_find_mmin}" = 1 ]]; then
  344. matches=$("${_find_command}" "${1}" -mmin +"${2}" 2> /dev/null)
  345. find_exit_code="${?}"
  346. if [[ -n "${matches}" ]]; then
  347. return 0
  348. else
  349. if [[ "${find_exit_code}" != 0 ]]; then
  350. _have_find_mmin=0
  351. else
  352. return 1
  353. fi
  354. fi
  355. fi
  356. # try perl, solaris ships with perl
  357. if command -v perl > /dev/null; then
  358. perl -e '((time - (stat("'"${1}"'"))[9]) / 60) > '"${2}"' && exit(0) || exit(1)'
  359. return "${?}"
  360. else
  361. echo >&2
  362. echo "WARNING: neither a find that supports -mmin (such as GNU find) or perl is available, disabling remote status checking. Install GNU find as gfind or perl to enable this feature, or set GIT_PROMPT_FETCH_REMOTE_STATUS=0 to disable this warning." >&2
  363. echo >&2
  364. GIT_PROMPT_FETCH_REMOTE_STATUS=0
  365. return 1
  366. fi
  367. }
  368. function checkUpstream() {
  369. local GIT_PROMPT_FETCH_TIMEOUT
  370. git_prompt_config
  371. local FETCH_HEAD="${repo}/.git/FETCH_HEAD"
  372. # Fech repo if local is stale for more than $GIT_FETCH_TIMEOUT minutes
  373. if [[ ! -e "${FETCH_HEAD}" ]] || olderThanMinutes "${FETCH_HEAD}" "${GIT_PROMPT_FETCH_TIMEOUT}"
  374. then
  375. if [[ -n $(git remote show) ]]; then
  376. (
  377. async_run "GIT_TERMINAL_PROMPT=0 git fetch --quiet"
  378. disown -h
  379. )
  380. fi
  381. fi
  382. }
  383. function replaceSymbols() {
  384. # Disable globbing, so a * could be used as symbol here
  385. set -f
  386. if [[ -z ${GIT_PROMPT_SYMBOLS_NO_REMOTE_TRACKING+x} ]]; then
  387. GIT_PROMPT_SYMBOLS_NO_REMOTE_TRACKING=L
  388. fi
  389. local VALUE="${1//_AHEAD_/${GIT_PROMPT_SYMBOLS_AHEAD}}"
  390. local VALUE1="${VALUE//_BEHIND_/${GIT_PROMPT_SYMBOLS_BEHIND}}"
  391. local VALUE2="${VALUE1//_NO_REMOTE_TRACKING_/${GIT_PROMPT_SYMBOLS_NO_REMOTE_TRACKING}}"
  392. echo "${VALUE2//_PREHASH_/${GIT_PROMPT_SYMBOLS_PREHASH}}"
  393. # reenable globbing symbols
  394. set +f
  395. }
  396. function createPrivateIndex {
  397. # Create a copy of the index to avoid conflicts with parallel git commands, e.g. git rebase.
  398. local __GIT_INDEX_FILE
  399. local __GIT_INDEX_PRIVATE
  400. if [[ -z "${GIT_INDEX_FILE+x}" ]]; then
  401. __GIT_INDEX_FILE="$(git rev-parse --git-dir)/index"
  402. else
  403. __GIT_INDEX_FILE="${GIT_INDEX_FILE}"
  404. fi
  405. __GIT_INDEX_PRIVATE="/tmp/git-index-private$$"
  406. command cp "${__GIT_INDEX_FILE}" "${__GIT_INDEX_PRIVATE}" 2>/dev/null
  407. echo "${__GIT_INDEX_PRIVATE}"
  408. }
  409. function updatePrompt() {
  410. local LAST_COMMAND_INDICATOR
  411. local PROMPT_LEADING_SPACE
  412. local PROMPT_START
  413. local PROMPT_END
  414. local EMPTY_PROMPT
  415. local Blue="\[\033[0;34m\]"
  416. git_prompt_config
  417. __GIT_PROMPT_IGNORE_STASH="${GIT_PROMPT_IGNORE_STASH:-0}"
  418. __GIT_PROMPT_SHOW_UPSTREAM="${GIT_PROMPT_SHOW_UPSTREAM:-0}"
  419. __GIT_PROMPT_IGNORE_SUBMODULES="${GIT_PROMPT_IGNORE_SUBMODULES:-0}"
  420. __GIT_PROMPT_WITH_USERNAME_AND_REPO="${GIT_PROMPT_WITH_USERNAME_AND_REPO:-0}"
  421. export __GIT_PROMPT_SHOW_UNTRACKED_FILES="${GIT_PROMPT_SHOW_UNTRACKED_FILES-normal}"
  422. local __GIT_PROMPT_SHOW_CHANGED_FILES_COUNT="${GIT_PROMPT_SHOW_CHANGED_FILES_COUNT:-1}"
  423. local GIT_INDEX_PRIVATE="$(createPrivateIndex)"
  424. #important to define GIT_INDEX_FILE as local: This way it only affects this function (and below) - even with the export afterwards
  425. local GIT_INDEX_FILE
  426. export GIT_INDEX_FILE="${GIT_INDEX_PRIVATE}"
  427. local -a git_status_fields
  428. while IFS=$'\n' read -r line; do git_status_fields+=("${line}"); done < <("${__GIT_STATUS_CMD}" 2>/dev/null)
  429. export GIT_BRANCH=$(replaceSymbols "${git_status_fields[0]}")
  430. local GIT_REMOTE="$(replaceSymbols "${git_status_fields[1]}")"
  431. if [[ "." == "${GIT_REMOTE}" ]]; then
  432. unset GIT_REMOTE
  433. fi
  434. local GIT_REMOTE_USERNAME_REPO="$(replaceSymbols "${git_status_fields[2]}")"
  435. if [[ "." == "${GIT_REMOTE_USERNAME_REPO}" ]]; then
  436. unset GIT_REMOTE_USERNAME_REPO
  437. fi
  438. local GIT_FORMATTED_UPSTREAM
  439. local GIT_UPSTREAM_PRIVATE="${git_status_fields[3]}"
  440. if [[ "${__GIT_PROMPT_SHOW_UPSTREAM:-0}" != "1" || "^" == "${GIT_UPSTREAM_PRIVATE}" ]]; then
  441. unset GIT_FORMATTED_UPSTREAM
  442. else
  443. GIT_FORMATTED_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/${GIT_UPSTREAM_PRIVATE}}"
  444. fi
  445. local GIT_STAGED="${git_status_fields[4]}"
  446. local GIT_CONFLICTS="${git_status_fields[5]}"
  447. local GIT_CHANGED="${git_status_fields[6]}"
  448. local GIT_UNTRACKED="${git_status_fields[7]}"
  449. local GIT_STASHED="${git_status_fields[8]}"
  450. local GIT_CLEAN="${git_status_fields[9]}"
  451. local NEW_PROMPT="${EMPTY_PROMPT}"
  452. if [[ "${#git_status_fields[@]}" -gt 0 ]]; then
  453. if [[ -z "${GIT_REMOTE_USERNAME_REPO+x}" ]]; then
  454. local GIT_PROMPT_PREFIX_FINAL="${GIT_PROMPT_PREFIX//_USERNAME_REPO_/${ResetColor}}"
  455. else
  456. if [[ -z "${GIT_PROMPT_USERNAME_REPO_SEPARATOR+x}" ]]; then
  457. local GIT_PROMPT_PREFIX_FINAL="${GIT_PROMPT_PREFIX//_USERNAME_REPO_/${GIT_REMOTE_USERNAME_REPO}${ResetColor}}"
  458. else
  459. local GIT_PROMPT_PREFIX_FINAL="${GIT_PROMPT_PREFIX//_USERNAME_REPO_/${GIT_REMOTE_USERNAME_REPO}${ResetColor}${GIT_PROMPT_USERNAME_REPO_SEPARATOR}}"
  460. fi
  461. fi
  462. case "${GIT_BRANCH-}" in
  463. ${GIT_PROMPT_MASTER_BRANCHES})
  464. local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX_FINAL}${GIT_PROMPT_MASTER_BRANCH}${URL_SHORT-}\${GIT_BRANCH}${ResetColor}${GIT_FORMATTED_UPSTREAM-}"
  465. ;;
  466. *)
  467. local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX_FINAL}${GIT_PROMPT_BRANCH}${URL_SHORT-}\${GIT_BRANCH}${ResetColor}${GIT_FORMATTED_UPSTREAM-}"
  468. ;;
  469. esac
  470. local STATUS=""
  471. # __add_status KIND VALEXPR INSERT
  472. # eg: __add_status 'STAGED' '-ne 0'
  473. __chk_gitvar_status() {
  474. local v
  475. if [[ "${2-}" = "-n" ]] ; then
  476. v="${2} \"\${GIT_${1}-}\""
  477. else
  478. v="\${GIT_${1}-} ${2}"
  479. fi
  480. if eval "[[ ${v} ]]" ; then
  481. if [[ "${3-}" != '-' ]] && [[ "${__GIT_PROMPT_SHOW_CHANGED_FILES_COUNT}" == "1" || "${1-}" == "REMOTE" ]]; then
  482. __add_status "\${GIT_PROMPT_${1}}\${GIT_${1}}\${ResetColor}"
  483. else
  484. __add_status "\${GIT_PROMPT_${1}}\${ResetColor}"
  485. fi
  486. fi
  487. }
  488. __add_gitvar_status() {
  489. __add_status "\${GIT_PROMPT_${1}}\${GIT_${1}}\${ResetColor}"
  490. }
  491. # __add_status SOMETEXT
  492. __add_status() {
  493. eval "STATUS=\"${STATUS}${1}\""
  494. }
  495. __chk_gitvar_status 'REMOTE' '-n'
  496. if [[ "${GIT_CLEAN}" -eq 0 ]] || [[ "${GIT_PROMPT_CLEAN}" != "" ]]; then
  497. __add_status "${GIT_PROMPT_SEPARATOR}"
  498. __chk_gitvar_status 'STAGED' '!= "0" && ${GIT_STAGED-} != "^"'
  499. __chk_gitvar_status 'CONFLICTS' '!= "0"'
  500. __chk_gitvar_status 'CHANGED' '!= "0"'
  501. __chk_gitvar_status 'UNTRACKED' '!= "0"'
  502. __chk_gitvar_status 'STASHED' '!= "0"'
  503. __chk_gitvar_status 'CLEAN' '= "1"' -
  504. fi
  505. __add_status "${ResetColor}${GIT_PROMPT_SUFFIX}"
  506. NEW_PROMPT="$(gp_add_virtualenv_to_prompt)${PROMPT_START}$(${prompt_callback})${STATUS_PREFIX}${STATUS}${PROMPT_END}"
  507. else
  508. NEW_PROMPT="${EMPTY_PROMPT}"
  509. fi
  510. PS1="${NEW_PROMPT//_LAST_COMMAND_INDICATOR_/${LAST_COMMAND_INDICATOR}${ResetColor}}"
  511. command rm "${GIT_INDEX_PRIVATE}" 2>/dev/null
  512. }
  513. # Helper function that returns virtual env information to be set in prompt
  514. # Honors virtualenvs own setting VIRTUAL_ENV_DISABLE_PROMPT
  515. function gp_add_virtualenv_to_prompt {
  516. local ACCUMULATED_VENV_PROMPT=""
  517. local VENV=""
  518. if [[ -n "${VIRTUAL_ENV-}" && -z "${VIRTUAL_ENV_DISABLE_PROMPT+x}" ]]; then
  519. VENV=$(basename "${VIRTUAL_ENV}")
  520. ACCUMULATED_VENV_PROMPT="${ACCUMULATED_VENV_PROMPT}${GIT_PROMPT_VIRTUALENV//_VIRTUALENV_/${VENV}}"
  521. fi
  522. if [[ -n "${NODE_VIRTUAL_ENV-}" && -z "${NODE_VIRTUAL_ENV_DISABLE_PROMPT+x}" ]]; then
  523. VENV=$(basename "${NODE_VIRTUAL_ENV}")
  524. ACCUMULATED_VENV_PROMPT="${ACCUMULATED_VENV_PROMPT}${GIT_PROMPT_VIRTUALENV//_VIRTUALENV_/${VENV}}"
  525. fi
  526. if [[ -n "${CONDA_DEFAULT_ENV-}" ]]; then
  527. VENV=$(basename "${CONDA_DEFAULT_ENV}")
  528. ACCUMULATED_VENV_PROMPT="${ACCUMULATED_VENV_PROMPT}${GIT_PROMPT_VIRTUALENV//_VIRTUALENV_/${VENV}}"
  529. fi
  530. echo "${ACCUMULATED_VENV_PROMPT}"
  531. }
  532. # Use exit status from declare command to determine whether input argument is a
  533. # bash function
  534. function is_function {
  535. declare -Ff "${1}" >/dev/null;
  536. }
  537. # Helper function that truncates $PWD depending on window width
  538. # Optionally specify maximum length as parameter (defaults to 1/3 of terminal)
  539. function gp_truncate_pwd {
  540. local tilde="~"
  541. local newPWD="${PWD/#${HOME}/${tilde}}"
  542. local pwdmaxlen="${1:-$((${COLUMNS:-80}/3))}"
  543. [[ "${#newPWD}" -gt "${pwdmaxlen}" ]] && newPWD="...${newPWD:3-$pwdmaxlen}"
  544. echo -n "${newPWD}"
  545. }
  546. # Sets the window title to the given argument string
  547. function gp_set_window_title {
  548. echo -ne "\[\033]0;"${@}"\007\]"
  549. }
  550. function prompt_callback_default {
  551. return
  552. }
  553. # toggle gitprompt
  554. function git_prompt_toggle() {
  555. if [[ "${GIT_PROMPT_DISABLE:-0}" = 1 ]]; then
  556. GIT_PROMPT_DISABLE=0
  557. else
  558. GIT_PROMPT_DISABLE=1
  559. fi
  560. return
  561. }
  562. function gp_install_prompt {
  563. if [[ -z "${OLD_GITPROMPT+x}" ]]; then
  564. OLD_GITPROMPT=${PS1}
  565. fi
  566. if [[ -z "${GIT_PROMPT_OLD_DIR_WAS_GIT+x}" ]]; then
  567. GIT_PROMPT_OLD_DIR_WAS_GIT=$(we_are_on_repo)
  568. fi
  569. if [[ -z "${PROMPT_COMMAND:+x}" ]]; then
  570. PROMPT_COMMAND=setGitPrompt
  571. else
  572. PROMPT_COMMAND="${PROMPT_COMMAND//$'\n'/;}" # convert all new lines to semi-colons
  573. PROMPT_COMMAND="${PROMPT_COMMAND#\;}" # remove leading semi-colon
  574. PROMPT_COMMAND="${PROMPT_COMMAND%% }" # remove trailing spaces
  575. PROMPT_COMMAND="${PROMPT_COMMAND%\;}" # remove trailing semi-colon
  576. local new_entry="setGitPrompt"
  577. case ";${PROMPT_COMMAND};" in
  578. *";${new_entry};"*)
  579. # echo "PROMPT_COMMAND already contains: $new_entry"
  580. :;;
  581. *)
  582. PROMPT_COMMAND="${PROMPT_COMMAND};${new_entry}"
  583. # echo "PROMPT_COMMAND does not contain: $new_entry"
  584. ;;
  585. esac
  586. fi
  587. local setLastCommandStateEntry="setLastCommandState"
  588. case ";${PROMPT_COMMAND};" in
  589. *";${setLastCommandStateEntry};"*)
  590. # echo "PROMPT_COMMAND already contains: $setLastCommandStateEntry"
  591. :;;
  592. *)
  593. PROMPT_COMMAND="${setLastCommandStateEntry};${PROMPT_COMMAND}"
  594. # echo "PROMPT_COMMAND does not contain: $setLastCommandStateEntry"
  595. ;;
  596. esac
  597. git_prompt_dir
  598. source "${__GIT_PROMPT_DIR}/git-prompt-help.sh"
  599. }
  600. gp_install_prompt