| 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/.profile: |
| 12 | # . ~/.git-prompt.sh # dot path/to/this-file |
| 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 | # See notes below about compatibility with other shells. |
| 34 | # |
| 35 | # The repository status will be displayed only if you are currently in a |
| 36 | # git repository. The %s token is the placeholder for the shown status. |
| 37 | # |
| 38 | # The prompt status always includes the current branch name. |
| 39 | # |
| 40 | # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value, |
| 41 | # unstaged (*) and staged (+) changes will be shown next to the branch |
| 42 | # name. You can configure this per-repository with the |
| 43 | # bash.showDirtyState variable, which defaults to true once |
| 44 | # GIT_PS1_SHOWDIRTYSTATE is enabled. |
| 45 | # |
| 46 | # You can also see if currently something is stashed, by setting |
| 47 | # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, |
| 48 | # then a '$' will be shown next to the branch name. |
| 49 | # |
| 50 | # If you would like to see if there're untracked files, then you can set |
| 51 | # GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked |
| 52 | # files, then a '%' will be shown next to the branch name. You can |
| 53 | # configure this per-repository with the bash.showUntrackedFiles |
| 54 | # variable, which defaults to true once GIT_PS1_SHOWUNTRACKEDFILES is |
| 55 | # enabled. |
| 56 | # |
| 57 | # If you would like to see the difference between HEAD and its upstream, |
| 58 | # set GIT_PS1_SHOWUPSTREAM="auto". A "<" indicates you are behind, ">" |
| 59 | # indicates you are ahead, "<>" indicates you have diverged and "=" |
| 60 | # indicates that there is no difference. You can further control |
| 61 | # behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list |
| 62 | # of values: |
| 63 | # |
| 64 | # verbose show number of commits ahead/behind (+/-) upstream |
| 65 | # name if verbose, then also show the upstream abbrev name |
| 66 | # legacy don't use the '--count' option available in recent |
| 67 | # versions of git-rev-list |
| 68 | # git always compare HEAD to @{upstream} |
| 69 | # svn always compare HEAD to your SVN upstream |
| 70 | # |
| 71 | # By default, __git_ps1 will compare HEAD to your SVN upstream if it can |
| 72 | # find one, or @{upstream} otherwise. Once you have set |
| 73 | # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by |
| 74 | # setting the bash.showUpstream config variable. |
| 75 | # |
| 76 | # You can change the separator between the branch name and the above |
| 77 | # state symbols by setting GIT_PS1_STATESEPARATOR. The default separator |
| 78 | # is SP. |
| 79 | # |
| 80 | # When there is an in-progress operation such as a merge, rebase, |
| 81 | # revert, cherry-pick, or bisect, the prompt will include information |
| 82 | # related to the operation, often in the form "|<OPERATION-NAME>". |
| 83 | # |
| 84 | # When the repository has a sparse-checkout, a notification of the form |
| 85 | # "|SPARSE" will be included in the prompt. This can be shortened to a |
| 86 | # single '?' character by setting GIT_PS1_COMPRESSSPARSESTATE, or omitted |
| 87 | # by setting GIT_PS1_OMITSPARSESTATE. |
| 88 | # |
| 89 | # If you would like to see a notification on the prompt when there are |
| 90 | # unresolved conflicts, set GIT_PS1_SHOWCONFLICTSTATE to "yes". The |
| 91 | # prompt will include "|CONFLICT". |
| 92 | # |
| 93 | # If you would like to see more information about the identity of |
| 94 | # commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE |
| 95 | # to one of these values: |
| 96 | # |
| 97 | # contains relative to newer annotated tag (v1.6.3.2~35) |
| 98 | # branch relative to newer tag or branch (master~4) |
| 99 | # describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f) |
| 100 | # tag relative to any older tag (v1.6.3.1-13-gdd42c2f) |
| 101 | # default exactly matching tag |
| 102 | # |
| 103 | # If you would like a colored hint about the current dirty state, set |
| 104 | # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on |
| 105 | # the colored output of "git status -sb". |
| 106 | # |
| 107 | # If you would like __git_ps1 to do nothing in the case when the current |
| 108 | # directory is set up to be ignored by git, then set |
| 109 | # GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the |
| 110 | # repository level by setting bash.hideIfPwdIgnored to "false". |
| 111 | # |
| 112 | # Compatibility with other shells (beyond bash/zsh): |
| 113 | # |
| 114 | # We require posix-ish shell plus "local" support, which is most |
| 115 | # shells (even pdksh), but excluding ksh93 (because no "local"). |
| 116 | # |
| 117 | # Prompt integration might differ between shells, but the gist is |
| 118 | # to load it once on shell init with '. path/to/git-prompt.sh', |
| 119 | # set GIT_PS1* vars once as needed, and either place $(__git_ps1..) |
| 120 | # inside PS1 once (0/1 args), or, before each prompt is displayed, |
| 121 | # call __git_ps1 (2/3 args) which sets PS1 with the status embedded. |
| 122 | # |
| 123 | # Many shells support the 1st method of command substitution, |
| 124 | # though some might need to first enable cmd substitution in PS1. |
| 125 | # |
| 126 | # When using colors, each escape sequence is wrapped between byte |
| 127 | # values 1 and 2 (control chars SOH, STX, respectively), which are |
| 128 | # invisible at the output, but for bash/readline they mark 0-width |
| 129 | # strings (SGR color sequences) when calculating the on-screen |
| 130 | # prompt width, to maintain correct input editing at the prompt. |
| 131 | # |
| 132 | # To replace or disable the 0-width markers, set GIT_PS1_COLOR_PRE |
| 133 | # and GIT_PS1_COLOR_POST to other markers, or empty (nul) to not |
| 134 | # use markers. For instance, some shells support '\[' and '\]' as |
| 135 | # start/end markers in PS1 - when invoking __git_ps1 with 3/4 args, |
| 136 | # but it may or may not work in command substitution mode. YMMV. |
| 137 | # |
| 138 | # If the shell doesn't support 0-width markers and editing behaves |
| 139 | # incorrectly when using colors in __git_ps1, then, other than |
| 140 | # disabling color, it might be solved using multi-line prompt, |
| 141 | # where the git status is not at the last line, e.g.: |
| 142 | # PS1='\n\w \u@\h$(__git_ps1 " (%s)")\n\$ ' |
| 143 | |
| 144 | # check whether printf supports -v |
| 145 | __git_printf_supports_v= |
| 146 | printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1 |
| 147 | |
| 148 | # like __git_SOH=$'\001' etc but works also in shells without $'...' |
| 149 | eval "$(printf ' |
| 150 | __git_SOH="\001" __git_STX="\002" __git_ESC="\033" |
| 151 | __git_LF="\n" __git_CRLF="\r\n" |
| 152 | ')" |
| 153 | |
| 154 | # stores the divergence from upstream in $p |
| 155 | # used by GIT_PS1_SHOWUPSTREAM |
| 156 | __git_ps1_show_upstream () |
| 157 | { |
| 158 | local key value |
| 159 | local svn_remotes="" svn_url_pattern="" count n |
| 160 | local upstream_type=git legacy="" verbose="" name="" |
| 161 | local LF="$__git_LF" |
| 162 | |
| 163 | # get some config options from git-config |
| 164 | local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')" |
| 165 | while read -r key value; do |
| 166 | case "$key" in |
| 167 | bash.showupstream) |
| 168 | GIT_PS1_SHOWUPSTREAM="$value" |
| 169 | if [ -z "${GIT_PS1_SHOWUPSTREAM}" ]; then |
| 170 | p="" |
| 171 | return |
| 172 | fi |
| 173 | ;; |
| 174 | svn-remote.*.url) |
| 175 | svn_remotes=${svn_remotes}${value}${LF} # URI\nURI\n... |
| 176 | svn_url_pattern="$svn_url_pattern\\|$value" |
| 177 | upstream_type=svn+git # default upstream type is SVN if available, else git |
| 178 | ;; |
| 179 | esac |
| 180 | done <<-OUTPUT |
| 181 | $output |
| 182 | OUTPUT |
| 183 | |
| 184 | # parse configuration values |
| 185 | local option |
| 186 | for option in ${GIT_PS1_SHOWUPSTREAM-}; do |
| 187 | case "$option" in |
| 188 | git|svn) upstream_type="$option" ;; |
| 189 | verbose) verbose=1 ;; |
| 190 | legacy) legacy=1 ;; |
| 191 | name) name=1 ;; |
| 192 | esac |
| 193 | done |
| 194 | |
| 195 | # Find our upstream type |
| 196 | case "$upstream_type" in |
| 197 | git) upstream_type="@{upstream}" ;; |
| 198 | svn*) |
| 199 | # successful svn-upstream resolution: |
| 200 | # - get the list of configured svn-remotes ($svn_remotes set above) |
| 201 | # - get the last commit which seems from one of our svn-remotes |
| 202 | # - confirm that it is from one of the svn-remotes |
| 203 | # - use $GIT_SVN_ID if set, else "git-svn" |
| 204 | |
| 205 | # get upstream from "git-svn-id: UPSTRM@N HASH" in a commit message |
| 206 | # (git-svn uses essentially the same procedure internally) |
| 207 | local svn_upstream="$( |
| 208 | git log --first-parent -1 \ |
| 209 | --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null |
| 210 | )" |
| 211 | |
| 212 | if [ -n "$svn_upstream" ]; then |
| 213 | # extract the URI, assuming --grep matched the last line |
| 214 | svn_upstream=${svn_upstream##*$LF} # last line |
| 215 | svn_upstream=${svn_upstream#*: } # UPSTRM@N HASH |
| 216 | svn_upstream=${svn_upstream%@*} # UPSTRM |
| 217 | |
| 218 | case ${LF}${svn_remotes} in |
| 219 | *"${LF}${svn_upstream}${LF}"*) |
| 220 | # grep indeed matched the last line - it's our remote |
| 221 | # default branch name for checkouts with no layout: |
| 222 | upstream_type=${GIT_SVN_ID:-git-svn} |
| 223 | ;; |
| 224 | *) |
| 225 | # the commit message includes one of our remotes, but |
| 226 | # it's not at the last line. is $svn_upstream junk? |
| 227 | upstream_type=${svn_upstream#/} |
| 228 | ;; |
| 229 | esac |
| 230 | elif [ "svn+git" = "$upstream_type" ]; then |
| 231 | upstream_type="@{upstream}" |
| 232 | fi |
| 233 | ;; |
| 234 | esac |
| 235 | |
| 236 | # Find how many commits we are ahead/behind our upstream |
| 237 | if [ -z "$legacy" ]; then |
| 238 | count="$(git rev-list --count --left-right \ |
| 239 | "$upstream_type"...HEAD 2>/dev/null)" |
| 240 | else |
| 241 | # produce equivalent output to --count for older versions of git |
| 242 | local commits |
| 243 | if commits="$(git rev-list --left-right "$upstream_type"...HEAD 2>/dev/null)" |
| 244 | then |
| 245 | local commit behind=0 ahead=0 |
| 246 | for commit in $commits |
| 247 | do |
| 248 | case "$commit" in |
| 249 | "<"*) behind=$((behind+1)) ;; |
| 250 | *) ahead=$((ahead+1)) ;; |
| 251 | esac |
| 252 | done |
| 253 | count="$behind $ahead" |
| 254 | else |
| 255 | count="" |
| 256 | fi |
| 257 | fi |
| 258 | |
| 259 | # calculate the result |
| 260 | if [ -z "$verbose" ]; then |
| 261 | case "$count" in |
| 262 | "") # no upstream |
| 263 | p="" ;; |
| 264 | "0 0") # equal to upstream |
| 265 | p="=" ;; |
| 266 | "0 "*) # ahead of upstream |
| 267 | p=">" ;; |
| 268 | *" 0") # behind upstream |
| 269 | p="<" ;; |
| 270 | *) # diverged from upstream |
| 271 | p="<>" ;; |
| 272 | esac |
| 273 | else # verbose, set upstream instead of p |
| 274 | case "$count" in |
| 275 | "") # no upstream |
| 276 | upstream="" ;; |
| 277 | "0 0") # equal to upstream |
| 278 | upstream="|u=" ;; |
| 279 | "0 "*) # ahead of upstream |
| 280 | upstream="|u+${count#0 }" ;; |
| 281 | *" 0") # behind upstream |
| 282 | upstream="|u-${count% 0}" ;; |
| 283 | *) # diverged from upstream |
| 284 | upstream="|u+${count#* }-${count% *}" ;; |
| 285 | esac |
| 286 | if [ -n "$count" ] && [ -n "$name" ]; then |
| 287 | __git_ps1_upstream_name=$(git rev-parse \ |
| 288 | --abbrev-ref "$upstream_type" 2>/dev/null) |
| 289 | if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then |
| 290 | upstream="$upstream \${__git_ps1_upstream_name}" |
| 291 | else |
| 292 | upstream="$upstream ${__git_ps1_upstream_name}" |
| 293 | # not needed anymore; keep user's |
| 294 | # environment clean |
| 295 | unset __git_ps1_upstream_name |
| 296 | fi |
| 297 | fi |
| 298 | fi |
| 299 | |
| 300 | } |
| 301 | |
| 302 | # Helper function that is meant to be called from __git_ps1. It |
| 303 | # injects color codes into the appropriate gitstring variables used |
| 304 | # to build a gitstring. Colored variables are responsible for clearing |
| 305 | # their own color. |
| 306 | __git_ps1_colorize_gitstring () |
| 307 | { |
| 308 | if [ -n "${ZSH_VERSION-}" ]; then |
| 309 | local c_red='%F{red}' |
| 310 | local c_green='%F{green}' |
| 311 | local c_lblue='%F{blue}' |
| 312 | local c_clear='%f' |
| 313 | else |
| 314 | # \001 (SOH) and \002 (STX) are 0-width substring markers |
| 315 | # which bash/readline identify while calculating the prompt |
| 316 | # on-screen width - to exclude 0-screen-width esc sequences. |
| 317 | local c_pre="${GIT_PS1_COLOR_PRE-$__git_SOH}${__git_ESC}[" |
| 318 | local c_post="m${GIT_PS1_COLOR_POST-$__git_STX}" |
| 319 | |
| 320 | local c_red="${c_pre}31${c_post}" |
| 321 | local c_green="${c_pre}32${c_post}" |
| 322 | local c_lblue="${c_pre}1;34${c_post}" |
| 323 | local c_clear="${c_pre}0${c_post}" |
| 324 | fi |
| 325 | local bad_color="$c_red" |
| 326 | local ok_color="$c_green" |
| 327 | local flags_color="$c_lblue" |
| 328 | |
| 329 | local branch_color="" |
| 330 | if [ "$detached" = no ]; then |
| 331 | branch_color="$ok_color" |
| 332 | else |
| 333 | branch_color="$bad_color" |
| 334 | fi |
| 335 | if [ -n "$c" ]; then |
| 336 | c="$branch_color$c$c_clear" |
| 337 | fi |
| 338 | b="$branch_color$b$c_clear" |
| 339 | |
| 340 | if [ -n "$w" ]; then |
| 341 | w="$bad_color$w$c_clear" |
| 342 | fi |
| 343 | if [ -n "$i" ]; then |
| 344 | i="$ok_color$i$c_clear" |
| 345 | fi |
| 346 | if [ -n "$s" ]; then |
| 347 | s="$flags_color$s$c_clear" |
| 348 | fi |
| 349 | if [ -n "$u" ]; then |
| 350 | u="$bad_color$u$c_clear" |
| 351 | fi |
| 352 | } |
| 353 | |
| 354 | # Helper function to read the first line of a file into a variable. |
| 355 | # __git_eread requires 2 arguments, the file path and the name of the |
| 356 | # variable, in that order. |
| 357 | __git_eread () |
| 358 | { |
| 359 | test -r "$1" && IFS=$__git_CRLF read -r "$2" <"$1" |
| 360 | } |
| 361 | |
| 362 | # see if a cherry-pick or revert is in progress, if the user has committed a |
| 363 | # conflict resolution with 'git commit' in the middle of a sequence of picks or |
| 364 | # reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read |
| 365 | # the todo file. |
| 366 | __git_sequencer_status () |
| 367 | { |
| 368 | local todo |
| 369 | if test -f "$g/CHERRY_PICK_HEAD" |
| 370 | then |
| 371 | r="|CHERRY-PICKING" |
| 372 | return 0; |
| 373 | elif test -f "$g/REVERT_HEAD" |
| 374 | then |
| 375 | r="|REVERTING" |
| 376 | return 0; |
| 377 | elif __git_eread "$g/sequencer/todo" todo |
| 378 | then |
| 379 | case "$todo" in |
| 380 | p[\ \ ]|pick[\ \ ]*) |
| 381 | r="|CHERRY-PICKING" |
| 382 | return 0 |
| 383 | ;; |
| 384 | revert[\ \ ]*) |
| 385 | r="|REVERTING" |
| 386 | return 0 |
| 387 | ;; |
| 388 | esac |
| 389 | fi |
| 390 | return 1 |
| 391 | } |
| 392 | |
| 393 | # __git_ps1 accepts 0 or 1 arguments (i.e., format string) |
| 394 | # when called from PS1 using command substitution |
| 395 | # in this mode it prints text to add to bash PS1 prompt (includes branch name) |
| 396 | # |
| 397 | # __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc) |
| 398 | # in that case it _sets_ PS1. The arguments are parts of a PS1 string. |
| 399 | # when two arguments are given, the first is prepended and the second appended |
| 400 | # to the state string when assigned to PS1. |
| 401 | # The optional third parameter will be used as printf format string to further |
| 402 | # customize the output of the git-status string. |
| 403 | # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true |
| 404 | __git_ps1 () |
| 405 | { |
| 406 | # preserve exit status |
| 407 | local exit="$?" |
| 408 | local pcmode=no |
| 409 | local detached=no |
| 410 | local ps1pc_start='\u@\h:\w ' |
| 411 | local ps1pc_end='\$ ' |
| 412 | local printf_format=' (%s)' |
| 413 | |
| 414 | case "$#" in |
| 415 | 2|3) pcmode=yes |
| 416 | ps1pc_start="$1" |
| 417 | ps1pc_end="$2" |
| 418 | printf_format="${3:-$printf_format}" |
| 419 | # set PS1 to a plain prompt so that we can |
| 420 | # simply return early if the prompt should not |
| 421 | # be decorated |
| 422 | PS1="$ps1pc_start$ps1pc_end" |
| 423 | ;; |
| 424 | 0|1) printf_format="${1:-$printf_format}" |
| 425 | ;; |
| 426 | *) return "$exit" |
| 427 | ;; |
| 428 | esac |
| 429 | |
| 430 | # ps1_expanded: This variable is set to 'yes' if the shell |
| 431 | # subjects the value of PS1 to parameter expansion: |
| 432 | # |
| 433 | # * bash does unless the promptvars option is disabled |
| 434 | # * zsh does not unless the PROMPT_SUBST option is set |
| 435 | # * POSIX shells always do |
| 436 | # |
| 437 | # If the shell would expand the contents of PS1 when drawing |
| 438 | # the prompt, a raw ref name must not be included in PS1. |
| 439 | # This protects the user from arbitrary code execution via |
| 440 | # specially crafted ref names. For example, a ref named |
| 441 | # 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the |
| 442 | # shell to execute 'sudo rm -rf /' when the prompt is drawn. |
| 443 | # |
| 444 | # Instead, the ref name should be placed in a separate global |
| 445 | # variable (in the __git_ps1_* namespace to avoid colliding |
| 446 | # with the user's environment) and that variable should be |
| 447 | # referenced from PS1. For example: |
| 448 | # |
| 449 | # __git_ps1_foo=$(do_something_to_get_ref_name) |
| 450 | # PS1="...stuff...\${__git_ps1_foo}...stuff..." |
| 451 | # |
| 452 | # If the shell does not expand the contents of PS1, the raw |
| 453 | # ref name must be included in PS1. |
| 454 | # |
| 455 | # The value of this variable is only relevant when in pcmode. |
| 456 | # |
| 457 | # Assume that the shell follows the POSIX specification and |
| 458 | # expands PS1 unless determined otherwise. (This is more |
| 459 | # likely to be correct if the user has a non-bash, non-zsh |
| 460 | # shell and safer than the alternative if the assumption is |
| 461 | # incorrect.) |
| 462 | # |
| 463 | local ps1_expanded=yes |
| 464 | [ -z "${ZSH_VERSION-}" ] || eval '[[ -o PROMPT_SUBST ]]' || ps1_expanded=no |
| 465 | [ -z "${BASH_VERSION-}" ] || shopt -q promptvars || ps1_expanded=no |
| 466 | |
| 467 | local repo_info rev_parse_exit_code |
| 468 | repo_info="$(git rev-parse --git-dir --is-inside-git-dir \ |
| 469 | --is-bare-repository --is-inside-work-tree --show-ref-format \ |
| 470 | --short HEAD 2>/dev/null)" |
| 471 | rev_parse_exit_code="$?" |
| 472 | |
| 473 | if [ -z "$repo_info" ]; then |
| 474 | return "$exit" |
| 475 | fi |
| 476 | |
| 477 | local LF="$__git_LF" |
| 478 | local short_sha="" |
| 479 | if [ "$rev_parse_exit_code" = "0" ]; then |
| 480 | short_sha="${repo_info##*$LF}" |
| 481 | repo_info="${repo_info%$LF*}" |
| 482 | fi |
| 483 | local ref_format="${repo_info##*$LF}" |
| 484 | repo_info="${repo_info%$LF*}" |
| 485 | local inside_worktree="${repo_info##*$LF}" |
| 486 | repo_info="${repo_info%$LF*}" |
| 487 | local bare_repo="${repo_info##*$LF}" |
| 488 | repo_info="${repo_info%$LF*}" |
| 489 | local inside_gitdir="${repo_info##*$LF}" |
| 490 | local g="${repo_info%$LF*}" |
| 491 | |
| 492 | if [ "true" = "$inside_worktree" ] && |
| 493 | [ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] && |
| 494 | [ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] && |
| 495 | git check-ignore -q . |
| 496 | then |
| 497 | return "$exit" |
| 498 | fi |
| 499 | |
| 500 | local sparse="" |
| 501 | if [ -z "${GIT_PS1_COMPRESSSPARSESTATE-}" ] && |
| 502 | [ -z "${GIT_PS1_OMITSPARSESTATE-}" ] && |
| 503 | [ "$(git config --bool core.sparseCheckout)" = "true" ]; then |
| 504 | sparse="|SPARSE" |
| 505 | fi |
| 506 | |
| 507 | local r="" |
| 508 | local b="" |
| 509 | local step="" |
| 510 | local total="" |
| 511 | if [ -d "$g/rebase-merge" ]; then |
| 512 | __git_eread "$g/rebase-merge/head-name" b |
| 513 | __git_eread "$g/rebase-merge/msgnum" step |
| 514 | __git_eread "$g/rebase-merge/end" total |
| 515 | r="|REBASE" |
| 516 | else |
| 517 | if [ -d "$g/rebase-apply" ]; then |
| 518 | __git_eread "$g/rebase-apply/next" step |
| 519 | __git_eread "$g/rebase-apply/last" total |
| 520 | if [ -f "$g/rebase-apply/rebasing" ]; then |
| 521 | __git_eread "$g/rebase-apply/head-name" b |
| 522 | r="|REBASE" |
| 523 | elif [ -f "$g/rebase-apply/applying" ]; then |
| 524 | r="|AM" |
| 525 | else |
| 526 | r="|AM/REBASE" |
| 527 | fi |
| 528 | elif [ -f "$g/MERGE_HEAD" ]; then |
| 529 | r="|MERGING" |
| 530 | elif __git_sequencer_status; then |
| 531 | : |
| 532 | elif [ -f "$g/BISECT_LOG" ]; then |
| 533 | r="|BISECTING" |
| 534 | fi |
| 535 | |
| 536 | if [ -n "$b" ]; then |
| 537 | : |
| 538 | elif [ -h "$g/HEAD" ]; then |
| 539 | # symlink symbolic ref |
| 540 | b="$(git symbolic-ref HEAD 2>/dev/null)" |
| 541 | else |
| 542 | local head="" |
| 543 | |
| 544 | case "$ref_format" in |
| 545 | files) |
| 546 | if ! __git_eread "$g/HEAD" head; then |
| 547 | return "$exit" |
| 548 | fi |
| 549 | |
| 550 | case $head in |
| 551 | "ref: "*) |
| 552 | head="${head#ref: }" |
| 553 | ;; |
| 554 | *) |
| 555 | head="" |
| 556 | esac |
| 557 | ;; |
| 558 | *) |
| 559 | head="$(git symbolic-ref HEAD 2>/dev/null)" |
| 560 | ;; |
| 561 | esac |
| 562 | |
| 563 | if test -z "$head"; then |
| 564 | detached=yes |
| 565 | b="$( |
| 566 | case "${GIT_PS1_DESCRIBE_STYLE-}" in |
| 567 | (contains) |
| 568 | git describe --contains HEAD ;; |
| 569 | (branch) |
| 570 | git describe --contains --all HEAD ;; |
| 571 | (tag) |
| 572 | git describe --tags HEAD ;; |
| 573 | (describe) |
| 574 | git describe HEAD ;; |
| 575 | (* | default) |
| 576 | git describe --tags --exact-match HEAD ;; |
| 577 | esac 2>/dev/null)" || |
| 578 | |
| 579 | b="$short_sha..." |
| 580 | b="($b)" |
| 581 | else |
| 582 | b="$head" |
| 583 | fi |
| 584 | fi |
| 585 | fi |
| 586 | |
| 587 | if [ -n "$step" ] && [ -n "$total" ]; then |
| 588 | r="$r $step/$total" |
| 589 | fi |
| 590 | |
| 591 | local conflict="" # state indicator for unresolved conflicts |
| 592 | if [ "${GIT_PS1_SHOWCONFLICTSTATE-}" = "yes" ] && |
| 593 | [ "$(git ls-files --unmerged 2>/dev/null)" ]; then |
| 594 | conflict="|CONFLICT" |
| 595 | fi |
| 596 | |
| 597 | local w="" |
| 598 | local i="" |
| 599 | local s="" |
| 600 | local u="" |
| 601 | local h="" |
| 602 | local c="" |
| 603 | local p="" # short version of upstream state indicator |
| 604 | local upstream="" # verbose version of upstream state indicator |
| 605 | |
| 606 | if [ "true" = "$inside_gitdir" ]; then |
| 607 | if [ "true" = "$bare_repo" ]; then |
| 608 | c="BARE:" |
| 609 | else |
| 610 | b="GIT_DIR!" |
| 611 | fi |
| 612 | elif [ "true" = "$inside_worktree" ]; then |
| 613 | if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] && |
| 614 | [ "$(git config --bool bash.showDirtyState)" != "false" ] |
| 615 | then |
| 616 | git diff --no-ext-diff --quiet || w="*" |
| 617 | git diff --no-ext-diff --cached --quiet || i="+" |
| 618 | if [ -z "$short_sha" ] && [ -z "$i" ]; then |
| 619 | i="#" |
| 620 | fi |
| 621 | fi |
| 622 | if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] && |
| 623 | git rev-parse --verify --quiet refs/stash >/dev/null |
| 624 | then |
| 625 | s="$" |
| 626 | fi |
| 627 | |
| 628 | if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && |
| 629 | [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && |
| 630 | git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null |
| 631 | then |
| 632 | u="%${ZSH_VERSION+%}" |
| 633 | fi |
| 634 | |
| 635 | if [ -n "${GIT_PS1_COMPRESSSPARSESTATE-}" ] && |
| 636 | [ "$(git config --bool core.sparseCheckout)" = "true" ]; then |
| 637 | h="?" |
| 638 | fi |
| 639 | |
| 640 | if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then |
| 641 | __git_ps1_show_upstream |
| 642 | fi |
| 643 | fi |
| 644 | |
| 645 | local z="${GIT_PS1_STATESEPARATOR- }" |
| 646 | |
| 647 | b=${b##refs/heads/} |
| 648 | if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then |
| 649 | __git_ps1_branch_name=$b |
| 650 | b="\${__git_ps1_branch_name}" |
| 651 | fi |
| 652 | |
| 653 | if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then |
| 654 | __git_ps1_colorize_gitstring |
| 655 | fi |
| 656 | |
| 657 | local f="$h$w$i$s$u$p" |
| 658 | local gitstring="$c$b${f:+$z$f}${sparse}$r${upstream}${conflict}" |
| 659 | |
| 660 | if [ "$pcmode" = yes ]; then |
| 661 | if [ "${__git_printf_supports_v-}" != yes ]; then |
| 662 | gitstring=$(printf -- "$printf_format" "$gitstring") |
| 663 | else |
| 664 | printf -v gitstring -- "$printf_format" "$gitstring" |
| 665 | fi |
| 666 | PS1="$ps1pc_start$gitstring$ps1pc_end" |
| 667 | else |
| 668 | printf -- "$printf_format" "$gitstring" |
| 669 | fi |
| 670 | |
| 671 | return "$exit" |
| 672 | } |