git-prompt.sh 13 KB

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