git-prompt.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. # bash/zsh git prompt support
  2. #
  3. # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
  4. # Distributed under the GNU General Public License, version 2.0.
  5. #
  6. # This script allows you to see the current branch in your prompt.
  7. #
  8. # To enable:
  9. #
  10. # 1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
  11. # 2) Add the following line to your .bashrc/.zshrc:
  12. # source ~/.git-prompt.sh
  13. # 3a) Change your PS1 to call __git_ps1 as
  14. # command-substitution:
  15. # Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
  16. # ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
  17. # the optional argument will be used as format string.
  18. # 3b) Alternatively, if you are using bash, __git_ps1 can be
  19. # used for PROMPT_COMMAND with two parameters, <pre> and
  20. # <post>, which are strings you would put in $PS1 before
  21. # and after the status string generated by the git-prompt
  22. # machinery. e.g.
  23. # PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
  24. # will show username, at-sign, host, colon, cwd, then
  25. # various status string, followed by dollar and SP, as
  26. # your prompt.
  27. # Optionally, you can supply a third argument with a printf
  28. # format string to finetune the output of the branch status
  29. #
  30. # The argument to __git_ps1 will be displayed only if you are currently
  31. # in a git repository. The %s token will be the name of the current
  32. # branch.
  33. #
  34. # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
  35. # unstaged (*) and staged (+) changes will be shown next to the branch
  36. # name. You can configure this per-repository with the
  37. # bash.showDirtyState variable, which defaults to true once
  38. # GIT_PS1_SHOWDIRTYSTATE is enabled.
  39. #
  40. # You can also see if currently something is stashed, by setting
  41. # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
  42. # then a '$' will be shown next to the branch name.
  43. #
  44. # If you would like to see if there're untracked files, then you can set
  45. # GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked
  46. # files, then a '%' will be shown next to the branch name. You can
  47. # configure this per-repository with the bash.showUntrackedFiles
  48. # variable, which defaults to true once GIT_PS1_SHOWUNTRACKEDFILES is
  49. # enabled.
  50. #
  51. # If you would like to see the difference between HEAD and its upstream,
  52. # set GIT_PS1_SHOWUPSTREAM="auto". A "<" indicates you are behind, ">"
  53. # indicates you are ahead, "<>" indicates you have diverged and "="
  54. # indicates that there is no difference. You can further control
  55. # behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list
  56. # of values:
  57. #
  58. # verbose show number of commits ahead/behind (+/-) upstream
  59. # legacy don't use the '--count' option available in recent
  60. # versions of git-rev-list
  61. # git always compare HEAD to @{upstream}
  62. # svn always compare HEAD to your SVN upstream
  63. #
  64. # By default, __git_ps1 will compare HEAD to your SVN upstream if it can
  65. # find one, or @{upstream} otherwise. Once you have set
  66. # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
  67. # setting the bash.showUpstream config variable.
  68. #
  69. # If you would like to see more information about the identity of
  70. # commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE
  71. # to one of these values:
  72. #
  73. # contains relative to newer annotated tag (v1.6.3.2~35)
  74. # branch relative to newer tag or branch (master~4)
  75. # describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
  76. # default exactly matching tag
  77. #
  78. # If you would like a colored hint about the current dirty state, set
  79. # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
  80. # the colored output of "git status -sb".
  81. # __gitdir accepts 0 or 1 arguments (i.e., location)
  82. # returns location of .git repo
  83. __gitdir ()
  84. {
  85. # Note: this function is duplicated in git-completion.bash
  86. # When updating it, make sure you update the other one to match.
  87. if [ -z "${1-}" ]; then
  88. if [ -n "${__git_dir-}" ]; then
  89. echo "$__git_dir"
  90. elif [ -n "${GIT_DIR-}" ]; then
  91. test -d "${GIT_DIR-}" || return 1
  92. echo "$GIT_DIR"
  93. elif [ -d .git ]; then
  94. echo .git
  95. else
  96. git rev-parse --git-dir 2>/dev/null
  97. fi
  98. elif [ -d "$1/.git" ]; then
  99. echo "$1/.git"
  100. else
  101. echo "$1"
  102. fi
  103. }
  104. # stores the divergence from upstream in $p
  105. # used by GIT_PS1_SHOWUPSTREAM
  106. __git_ps1_show_upstream ()
  107. {
  108. local key value
  109. local svn_remote svn_url_pattern count n
  110. local upstream=git legacy="" verbose=""
  111. svn_remote=()
  112. # get some config options from git-config
  113. local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
  114. while read -r key value; do
  115. case "$key" in
  116. bash.showupstream)
  117. GIT_PS1_SHOWUPSTREAM="$value"
  118. if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
  119. p=""
  120. return
  121. fi
  122. ;;
  123. svn-remote.*.url)
  124. svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
  125. svn_url_pattern+="\\|$value"
  126. upstream=svn+git # default upstream is SVN if available, else git
  127. ;;
  128. esac
  129. done <<< "$output"
  130. # parse configuration values
  131. for option in ${GIT_PS1_SHOWUPSTREAM}; do
  132. case "$option" in
  133. git|svn) upstream="$option" ;;
  134. verbose) verbose=1 ;;
  135. legacy) legacy=1 ;;
  136. esac
  137. done
  138. # Find our upstream
  139. case "$upstream" in
  140. git) upstream="@{upstream}" ;;
  141. svn*)
  142. # get the upstream from the "git-svn-id: ..." in a commit message
  143. # (git-svn uses essentially the same procedure internally)
  144. local svn_upstream=($(git log --first-parent -1 \
  145. --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
  146. if [[ 0 -ne ${#svn_upstream[@]} ]]; then
  147. svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
  148. svn_upstream=${svn_upstream%@*}
  149. local n_stop="${#svn_remote[@]}"
  150. for ((n=1; n <= n_stop; n++)); do
  151. svn_upstream=${svn_upstream#${svn_remote[$n]}}
  152. done
  153. if [[ -z "$svn_upstream" ]]; then
  154. # default branch name for checkouts with no layout:
  155. upstream=${GIT_SVN_ID:-git-svn}
  156. else
  157. upstream=${svn_upstream#/}
  158. fi
  159. elif [[ "svn+git" = "$upstream" ]]; then
  160. upstream="@{upstream}"
  161. fi
  162. ;;
  163. esac
  164. # Find how many commits we are ahead/behind our upstream
  165. if [[ -z "$legacy" ]]; then
  166. count="$(git rev-list --count --left-right \
  167. "$upstream"...HEAD 2>/dev/null)"
  168. else
  169. # produce equivalent output to --count for older versions of git
  170. local commits
  171. if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
  172. then
  173. local commit behind=0 ahead=0
  174. for commit in $commits
  175. do
  176. case "$commit" in
  177. "<"*) ((behind++)) ;;
  178. *) ((ahead++)) ;;
  179. esac
  180. done
  181. count="$behind $ahead"
  182. else
  183. count=""
  184. fi
  185. fi
  186. # calculate the result
  187. if [[ -z "$verbose" ]]; then
  188. case "$count" in
  189. "") # no upstream
  190. p="" ;;
  191. "0 0") # equal to upstream
  192. p="=" ;;
  193. "0 "*) # ahead of upstream
  194. p=">" ;;
  195. *" 0") # behind upstream
  196. p="<" ;;
  197. *) # diverged from upstream
  198. p="<>" ;;
  199. esac
  200. else
  201. case "$count" in
  202. "") # no upstream
  203. p="" ;;
  204. "0 0") # equal to upstream
  205. p=" u=" ;;
  206. "0 "*) # ahead of upstream
  207. p=" u+${count#0 }" ;;
  208. *" 0") # behind upstream
  209. p=" u-${count% 0}" ;;
  210. *) # diverged from upstream
  211. p=" u+${count#* }-${count% *}" ;;
  212. esac
  213. fi
  214. }
  215. # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
  216. # when called from PS1 using command substitution
  217. # in this mode it prints text to add to bash PS1 prompt (includes branch name)
  218. #
  219. # __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc)
  220. # in that case it _sets_ PS1. The arguments are parts of a PS1 string.
  221. # when two arguments are given, the first is prepended and the second appended
  222. # to the state string when assigned to PS1.
  223. # The optional third parameter will be used as printf format string to further
  224. # customize the output of the git-status string.
  225. # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
  226. __git_ps1 ()
  227. {
  228. local pcmode=no
  229. local detached=no
  230. local ps1pc_start='\u@\h:\w '
  231. local ps1pc_end='\$ '
  232. local printf_format=' (%s)'
  233. case "$#" in
  234. 2|3) pcmode=yes
  235. ps1pc_start="$1"
  236. ps1pc_end="$2"
  237. printf_format="${3:-$printf_format}"
  238. ;;
  239. 0|1) printf_format="${1:-$printf_format}"
  240. ;;
  241. *) return
  242. ;;
  243. esac
  244. local g="$(__gitdir)"
  245. if [ -z "$g" ]; then
  246. if [ $pcmode = yes ]; then
  247. #In PC mode PS1 always needs to be set
  248. PS1="$ps1pc_start$ps1pc_end"
  249. fi
  250. else
  251. local r=""
  252. local b=""
  253. if [ -f "$g/rebase-merge/interactive" ]; then
  254. r="|REBASE-i"
  255. b="$(cat "$g/rebase-merge/head-name")"
  256. elif [ -d "$g/rebase-merge" ]; then
  257. r="|REBASE-m"
  258. b="$(cat "$g/rebase-merge/head-name")"
  259. else
  260. if [ -d "$g/rebase-apply" ]; then
  261. if [ -f "$g/rebase-apply/rebasing" ]; then
  262. r="|REBASE"
  263. elif [ -f "$g/rebase-apply/applying" ]; then
  264. r="|AM"
  265. else
  266. r="|AM/REBASE"
  267. fi
  268. elif [ -f "$g/MERGE_HEAD" ]; then
  269. r="|MERGING"
  270. elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
  271. r="|CHERRY-PICKING"
  272. elif [ -f "$g/BISECT_LOG" ]; then
  273. r="|BISECTING"
  274. fi
  275. b="$(git symbolic-ref HEAD 2>/dev/null)" || {
  276. detached=yes
  277. b="$(
  278. case "${GIT_PS1_DESCRIBE_STYLE-}" in
  279. (contains)
  280. git describe --contains HEAD ;;
  281. (branch)
  282. git describe --contains --all HEAD ;;
  283. (describe)
  284. git describe HEAD ;;
  285. (* | default)
  286. git describe --tags --exact-match HEAD ;;
  287. esac 2>/dev/null)" ||
  288. b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
  289. b="unknown"
  290. b="($b)"
  291. }
  292. fi
  293. local w=""
  294. local i=""
  295. local s=""
  296. local u=""
  297. local c=""
  298. local p=""
  299. if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
  300. if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
  301. c="BARE:"
  302. else
  303. b="GIT_DIR!"
  304. fi
  305. elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
  306. if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
  307. [ "$(git config --bool bash.showDirtyState)" != "false" ]
  308. then
  309. git diff --no-ext-diff --quiet --exit-code || w="*"
  310. if git rev-parse --quiet --verify HEAD >/dev/null; then
  311. git diff-index --cached --quiet HEAD -- || i="+"
  312. else
  313. i="#"
  314. fi
  315. fi
  316. if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
  317. git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
  318. fi
  319. if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
  320. [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
  321. [ -n "$(git ls-files --others --exclude-standard)" ]
  322. then
  323. u="%%"
  324. fi
  325. if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
  326. __git_ps1_show_upstream
  327. fi
  328. fi
  329. local f="$w$i$s$u"
  330. if [ $pcmode = yes ]; then
  331. local gitstring=
  332. if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
  333. local c_red='\e[31m'
  334. local c_green='\e[32m'
  335. local c_lblue='\e[1;34m'
  336. local c_clear='\e[0m'
  337. local bad_color=$c_red
  338. local ok_color=$c_green
  339. local branch_color="$c_clear"
  340. local flags_color="$c_lblue"
  341. local branchstring="$c${b##refs/heads/}"
  342. if [ $detached = no ]; then
  343. branch_color="$ok_color"
  344. else
  345. branch_color="$bad_color"
  346. fi
  347. # Setting gitstring directly with \[ and \] around colors
  348. # is necessary to prevent wrapping issues!
  349. gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
  350. if [ -n "$w$i$s$u$r$p" ]; then
  351. gitstring="$gitstring "
  352. fi
  353. if [ "$w" = "*" ]; then
  354. gitstring="$gitstring\[$bad_color\]$w"
  355. fi
  356. if [ -n "$i" ]; then
  357. gitstring="$gitstring\[$ok_color\]$i"
  358. fi
  359. if [ -n "$s" ]; then
  360. gitstring="$gitstring\[$flags_color\]$s"
  361. fi
  362. if [ -n "$u" ]; then
  363. gitstring="$gitstring\[$bad_color\]$u"
  364. fi
  365. gitstring="$gitstring\[$c_clear\]$r$p"
  366. else
  367. gitstring="$c${b##refs/heads/}${f:+ $f}$r$p"
  368. fi
  369. gitstring=$(printf -- "$printf_format" "$gitstring")
  370. PS1="$ps1pc_start$gitstring$ps1pc_end"
  371. else
  372. # NO color option unless in PROMPT_COMMAND mode
  373. printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p"
  374. fi
  375. fi
  376. }