git-prompt.sh « completion « contrib - git.git (original) (raw)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672


# bash/zsh git prompt support
#
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
# Distributed under the GNU General Public License, version 2.0.
#
# This script allows you to see repository status in your prompt.
#
# To enable:
#
#    1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
#    2) Add the following line to your .bashrc/.zshrc/.profile:
#        . ~/.git-prompt.sh   # dot path/to/this-file
#    3a) Change your PS1 to call __git_ps1 as
#        command-substitution:
#        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
#        the optional argument will be used as format string.
#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
#        with two parameters, <pre> and <post>, which are strings
#        you would put in $PS1 before and after the status string
#        generated by the git-prompt machinery.  e.g.
#        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
#          will show username, at-sign, host, colon, cwd, then
#          various status string, followed by dollar and SP, as
#          your prompt.
#        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
#          will show username, pipe, then various status string,
#          followed by colon, cwd, dollar and SP, as your prompt.
#        Optionally, you can supply a third argument with a printf
#        format string to finetune the output of the branch status
#
#    See notes below about compatibility with other shells.
#
# The repository status will be displayed only if you are currently in a
# git repository. The %s token is the placeholder for the shown status.
#
# The prompt status always includes the current branch name.
#
# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
# unstaged (*) and staged (+) changes will be shown next to the branch
# name.  You can configure this per-repository with the
# bash.showDirtyState variable, which defaults to true once
# GIT_PS1_SHOWDIRTYSTATE is enabled.
#
# You can also see if currently something is stashed, by setting
# GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
# then a '$' will be shown next to the branch name.
#
# If you would like to see if there're untracked files, then you can set
# GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked
# files, then a '%' will be shown next to the branch name.  You can
# configure this per-repository with the bash.showUntrackedFiles
# variable, which defaults to true once GIT_PS1_SHOWUNTRACKEDFILES is
# enabled.
#
# If you would like to see the difference between HEAD and its upstream,
# set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates you are behind, ">"
# indicates you are ahead, "<>" indicates you have diverged and "="
# indicates that there is no difference. You can further control
# behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list
# of values:
#
#     verbose       show number of commits ahead/behind (+/-) upstream
#     name          if verbose, then also show the upstream abbrev name
#     legacy        don't use the '--count' option available in recent
#                   versions of git-rev-list
#     git           always compare HEAD to @{upstream}
#     svn           always compare HEAD to your SVN upstream
#
# By default, __git_ps1 will compare HEAD to your SVN upstream if it can
# find one, or @{upstream} otherwise.  Once you have set
# GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
# setting the bash.showUpstream config variable.
#
# You can change the separator between the branch name and the above
# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator
# is SP.
#
# When there is an in-progress operation such as a merge, rebase,
# revert, cherry-pick, or bisect, the prompt will include information
# related to the operation, often in the form "|<OPERATION-NAME>".
#
# When the repository has a sparse-checkout, a notification of the form
# "|SPARSE" will be included in the prompt.  This can be shortened to a
# single '?' character by setting GIT_PS1_COMPRESSSPARSESTATE, or omitted
# by setting GIT_PS1_OMITSPARSESTATE.
#
# If you would like to see a notification on the prompt when there are
# unresolved conflicts, set GIT_PS1_SHOWCONFLICTSTATE to "yes". The
# prompt will include "|CONFLICT".
#
# If you would like to see more information about the identity of
# commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE
# to one of these values:
#
#     contains      relative to newer annotated tag (v1.6.3.2~35)
#     branch        relative to newer tag or branch (master~4)
#     describe      relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
#     tag           relative to any older tag (v1.6.3.1-13-gdd42c2f)
#     default       exactly matching tag
#
# If you would like a colored hint about the current dirty state, set
# GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
# the colored output of "git status -sb".
#
# If you would like __git_ps1 to do nothing in the case when the current
# directory is set up to be ignored by git, then set
# GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the
# repository level by setting bash.hideIfPwdIgnored to "false".
#
# Compatibility with other shells (beyond bash/zsh):
#
#    We require posix-ish shell plus "local" support, which is most
#    shells (even pdksh), but excluding ksh93 (because no "local").
#
#    Prompt integration might differ between shells, but the gist is
#    to load it once on shell init with '. path/to/git-prompt.sh',
#    set GIT_PS1* vars once as needed, and either place $(__git_ps1..)
#    inside PS1 once (0/1 args), or, before each prompt is displayed,
#    call __git_ps1 (2/3 args) which sets PS1 with the status embedded.
#
#    Many shells support the 1st method of command substitution,
#    though some might need to first enable cmd substitution in PS1.
#
#    When using colors, each escape sequence is wrapped between byte
#    values 1 and 2 (control chars SOH, STX, respectively), which are
#    invisible at the output, but for bash/readline they mark 0-width
#    strings (SGR color sequences) when calculating the on-screen
#    prompt width, to maintain correct input editing at the prompt.
#
#    To replace or disable the 0-width markers, set GIT_PS1_COLOR_PRE
#    and GIT_PS1_COLOR_POST to other markers, or empty (nul) to not
#    use markers. For instance, some shells support '\[' and '\]' as
#    start/end markers in PS1 - when invoking __git_ps1 with 3/4 args,
#    but it may or may not work in command substitution mode. YMMV.
#
#    If the shell doesn't support 0-width markers and editing behaves
#    incorrectly when using colors in __git_ps1, then, other than
#    disabling color, it might be solved using multi-line prompt,
#    where the git status is not at the last line, e.g.:
#      PS1='\n\w \u@\h$(__git_ps1 " (%s)")\n\$ '

# check whether printf supports -v
__git_printf_supports_v=
printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1

# like __git_SOH=$'\001' etc but works also in shells without $'...'
eval "$(printf '
    __git_SOH="\001" __git_STX="\002" __git_ESC="\033"
    __git_LF="\n" __git_CRLF="\r\n"
')"

# stores the divergence from upstream in $p
# used by GIT_PS1_SHOWUPSTREAM
__git_ps1_show_upstream ()
{
    local key value
    local svn_remotes="" svn_url_pattern="" count n
    local upstream_type=git legacy="" verbose="" name=""
    local LF="$__git_LF"

    # get some config options from git-config
    local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
    while read -r key value; do
        case "$key" in
        bash.showupstream)
            GIT_PS1_SHOWUPSTREAM="$value"
            if [ -z "${GIT_PS1_SHOWUPSTREAM}" ]; then
                p=""
                return
            fi
            ;;
        svn-remote.*.url)
            svn_remotes=${svn_remotes}${value}${LF}  # URI\nURI\n...
            svn_url_pattern="$svn_url_pattern\\|$value"
            upstream_type=svn+git # default upstream type is SVN if available, else git
            ;;
        esac
    done <<-OUTPUT
        $output
    OUTPUT

    # parse configuration values
    local option
    for option in ${GIT_PS1_SHOWUPSTREAM-}; do
        case "$option" in
        git|svn) upstream_type="$option" ;;
        verbose) verbose=1 ;;
        legacy)  legacy=1  ;;
        name)    name=1 ;;
        esac
    done

    # Find our upstream type
    case "$upstream_type" in
    git)    upstream_type="@{upstream}" ;;
    svn*)
        # successful svn-upstream resolution:
        # - get the list of configured svn-remotes ($svn_remotes set above)
        # - get the last commit which seems from one of our svn-remotes
        # - confirm that it is from one of the svn-remotes
        # - use $GIT_SVN_ID if set, else "git-svn"

        # get upstream from "git-svn-id: UPSTRM@N HASH" in a commit message
        # (git-svn uses essentially the same procedure internally)
        local svn_upstream="$(
            git log --first-parent -1 \
                --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null
        )"

        if [ -n "$svn_upstream" ]; then
            # extract the URI, assuming --grep matched the last line
            svn_upstream=${svn_upstream##*$LF}  # last line
            svn_upstream=${svn_upstream#*: }    # UPSTRM@N HASH
            svn_upstream=${svn_upstream%@*}     # UPSTRM

            case <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mi>F</mi></mrow><annotation encoding="application/x-tex">{LF}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span></span></span></span></span>{svn_remotes} in
            *"${LF}${svn_upstream}${LF}"*)
                # grep indeed matched the last line - it's our remote
                # default branch name for checkouts with no layout:
                upstream_type=${GIT_SVN_ID:-git-svn}
                ;;
            *)
                # the commit message includes one of our remotes, but
                # it's not at the last line. is $svn_upstream junk?
                upstream_type=${svn_upstream#/}
                ;;
            esac
        elif [ "svn+git" = "$upstream_type" ]; then
            upstream_type="@{upstream}"
        fi
        ;;
    esac

    # Find how many commits we are ahead/behind our upstream
    if [ -z "$legacy" ]; then
        count="$(git rev-list --count --left-right \
                "$upstream_type"...HEAD 2>/dev/null)"
    else
        # produce equivalent output to --count for older versions of git
        local commits
        if commits="$(git rev-list --left-right "$upstream_type"...HEAD 2>/dev/null)"
        then
            local commit behind=0 ahead=0
            for commit in $commits
            do
                case "$commit" in
                "<"*) behind=$((behind+1)) ;;
                *)    ahead=$((ahead+1))   ;;
                esac
            done
            count="$behind	$ahead"
        else
            count=""
        fi
    fi

    # calculate the result
    if [ -z "$verbose" ]; then
        case "$count" in
        "") # no upstream
            p="" ;;
        "0	0") # equal to upstream
            p="=" ;;
        "0	"*) # ahead of upstream
            p=">" ;;
        *"	0") # behind upstream
            p="<" ;;
        *)	    # diverged from upstream
            p="<>" ;;
        esac
    else # verbose, set upstream instead of p
        case "$count" in
        "") # no upstream
            upstream="" ;;
        "0	0") # equal to upstream
            upstream="|u=" ;;
        "0	"*) # ahead of upstream
            upstream="|u+${count#0	}" ;;
        *"	0") # behind upstream
            upstream="|u-${count%	0}" ;;
        *)	    # diverged from upstream
            upstream="|u+${count#*	}-${count%	*}" ;;
        esac
        if [ -n "$count" ] && [ -n "$name" ]; then
            __git_ps1_upstream_name=$(git rev-parse \
                --abbrev-ref "$upstream_type" 2>/dev/null)
            if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
                upstream="$upstream \${__git_ps1_upstream_name}"
            else
                upstream="$upstream ${__git_ps1_upstream_name}"
                # not needed anymore; keep user's
                # environment clean
                unset __git_ps1_upstream_name
            fi
        fi
    fi

}

# Helper function that is meant to be called from __git_ps1.  It
# injects color codes into the appropriate gitstring variables used
# to build a gitstring. Colored variables are responsible for clearing
# their own color.
__git_ps1_colorize_gitstring ()
{
    if [ -n "${ZSH_VERSION-}" ]; then
        local c_red='%F{red}'
        local c_green='%F{green}'
        local c_lblue='%F{blue}'
        local c_clear='%f'
    else
        # \001 (SOH) and \002 (STX) are 0-width substring markers
        # which bash/readline identify while calculating the prompt
        # on-screen width - to exclude 0-screen-width esc sequences.
        local c_pre="${GIT_PS1_COLOR_PRE-$__git_SOH}${__git_ESC}["
        local c_post="m${GIT_PS1_COLOR_POST-$__git_STX}"

        local c_red="${c_pre}31${c_post}"
        local c_green="${c_pre}32${c_post}"
        local c_lblue="${c_pre}1;34${c_post}"
        local c_clear="${c_pre}0${c_post}"
    fi
    local bad_color="$c_red"
    local ok_color="$c_green"
    local flags_color="$c_lblue"

    local branch_color=""
    if [ "$detached" = no ]; then
        branch_color="$ok_color"
    else
        branch_color="$bad_color"
    fi
    if [ -n "$c" ]; then
        c="$branch_color$c$c_clear"
    fi
    b="$branch_color$b$c_clear"

    if [ -n "$w" ]; then
        w="$bad_color$w$c_clear"
    fi
    if [ -n "$i" ]; then
        i="$ok_color$i$c_clear"
    fi
    if [ -n "$s" ]; then
        s="$flags_color$s$c_clear"
    fi
    if [ -n "$u" ]; then
        u="$bad_color$u$c_clear"
    fi
}

# Helper function to read the first line of a file into a variable.
# __git_eread requires 2 arguments, the file path and the name of the
# variable, in that order.
__git_eread ()
{
    test -r "$1" && IFS=$__git_CRLF read -r "$2" <"$1"
}

# see if a cherry-pick or revert is in progress, if the user has committed a
# conflict resolution with 'git commit' in the middle of a sequence of picks or
# reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read
# the todo file.
__git_sequencer_status ()
{
    local todo
    if test -f "$g/CHERRY_PICK_HEAD"
    then
        r="|CHERRY-PICKING"
        return 0;
    elif test -f "$g/REVERT_HEAD"
    then
        r="|REVERTING"
        return 0;
    elif __git_eread "$g/sequencer/todo" todo
    then
        case "$todo" in
        p[\ \	]|pick[\ \	]*)
            r="|CHERRY-PICKING"
            return 0
        ;;
        revert[\ \	]*)
            r="|REVERTING"
            return 0
        ;;
        esac
    fi
    return 1
}

# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# when called from PS1 using command substitution
# in this mode it prints text to add to bash PS1 prompt (includes branch name)
#
# __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc)
# in that case it _sets_ PS1. The arguments are parts of a PS1 string.
# when two arguments are given, the first is prepended and the second appended
# to the state string when assigned to PS1.
# The optional third parameter will be used as printf format string to further
# customize the output of the git-status string.
# In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
__git_ps1 ()
{
    # preserve exit status
    local exit="$?"
    local pcmode=no
    local detached=no
    local ps1pc_start='\u@\h:\w '
    local ps1pc_end='\$ '
    local printf_format=' (%s)'

    case "$#" in
        2|3)	pcmode=yes
            ps1pc_start="$1"
            ps1pc_end="$2"
            printf_format="${3:-$printf_format}"
            # set PS1 to a plain prompt so that we can
            # simply return early if the prompt should not
            # be decorated
            PS1="$ps1pc_start$ps1pc_end"
        ;;
        0|1)	printf_format="${1:-$printf_format}"
        ;;
        *)	return "$exit"
        ;;
    esac

    # ps1_expanded:  This variable is set to 'yes' if the shell
    # subjects the value of PS1 to parameter expansion:
    #
    #   * bash does unless the promptvars option is disabled
    #   * zsh does not unless the PROMPT_SUBST option is set
    #   * POSIX shells always do
    #
    # If the shell would expand the contents of PS1 when drawing
    # the prompt, a raw ref name must not be included in PS1.
    # This protects the user from arbitrary code execution via
    # specially crafted ref names.  For example, a ref named
    # 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the
    # shell to execute 'sudo rm -rf /' when the prompt is drawn.
    #
    # Instead, the ref name should be placed in a separate global
    # variable (in the __git_ps1_* namespace to avoid colliding
    # with the user's environment) and that variable should be
    # referenced from PS1.  For example:
    #
    #     __git_ps1_foo=$(do_something_to_get_ref_name)
    #     PS1="...stuff...\${__git_ps1_foo}...stuff..."
    #
    # If the shell does not expand the contents of PS1, the raw
    # ref name must be included in PS1.
    #
    # The value of this variable is only relevant when in pcmode.
    #
    # Assume that the shell follows the POSIX specification and
    # expands PS1 unless determined otherwise.  (This is more
    # likely to be correct if the user has a non-bash, non-zsh
    # shell and safer than the alternative if the assumption is
    # incorrect.)
    #
    local ps1_expanded=yes
    [ -z "${ZSH_VERSION-}" ] || eval '[[ -o PROMPT_SUBST ]]' || ps1_expanded=no
    [ -z "${BASH_VERSION-}" ] || shopt -q promptvars || ps1_expanded=no

    local repo_info rev_parse_exit_code
    repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
        --is-bare-repository --is-inside-work-tree --show-ref-format \
        --short HEAD 2>/dev/null)"
    rev_parse_exit_code="$?"

    if [ -z "$repo_info" ]; then
        return "$exit"
    fi

    local LF="$__git_LF"
    local short_sha=""
    if [ "$rev_parse_exit_code" = "0" ]; then
        short_sha="${repo_info##*$LF}"
        repo_info="${repo_info%$LF*}"
    fi
    local ref_format="${repo_info##*$LF}"
    repo_info="${repo_info%$LF*}"
    local inside_worktree="${repo_info##*$LF}"
    repo_info="${repo_info%$LF*}"
    local bare_repo="${repo_info##*$LF}"
    repo_info="${repo_info%$LF*}"
    local inside_gitdir="${repo_info##*$LF}"
    local g="${repo_info%$LF*}"

    if [ "true" = "$inside_worktree" ] &&
       [ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] &&
       [ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] &&
       git check-ignore -q .
    then
        return "$exit"
    fi

    local sparse=""
    if [ -z "${GIT_PS1_COMPRESSSPARSESTATE-}" ] &&
       [ -z "${GIT_PS1_OMITSPARSESTATE-}" ] &&
       [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
        sparse="|SPARSE"
    fi

    local r=""
    local b=""
    local step=""
    local total=""
    if [ -d "$g/rebase-merge" ]; then
        __git_eread "$g/rebase-merge/head-name" b
        __git_eread "$g/rebase-merge/msgnum" step
        __git_eread "$g/rebase-merge/end" total
        r="|REBASE"
    else
        if [ -d "$g/rebase-apply" ]; then
            __git_eread "$g/rebase-apply/next" step
            __git_eread "$g/rebase-apply/last" total
            if [ -f "$g/rebase-apply/rebasing" ]; then
                __git_eread "$g/rebase-apply/head-name" b
                r="|REBASE"
            elif [ -f "$g/rebase-apply/applying" ]; then
                r="|AM"
            else
                r="|AM/REBASE"
            fi
        elif [ -f "$g/MERGE_HEAD" ]; then
            r="|MERGING"
        elif __git_sequencer_status; then
            :
        elif [ -f "$g/BISECT_LOG" ]; then
            r="|BISECTING"
        fi

        if [ -n "$b" ]; then
            :
        elif [ -h "$g/HEAD" ]; then
            # symlink symbolic ref
            b="$(git symbolic-ref HEAD 2>/dev/null)"
        else
            local head=""

            case "$ref_format" in
            files)
                if ! __git_eread "$g/HEAD" head; then
                    return "$exit"
                fi

                case $head in
                "ref: "*)
                    head="${head#ref: }"
                    ;;
                *)
                    head=""
                esac
                ;;
            *)
                head="$(git symbolic-ref HEAD 2>/dev/null)"
                ;;
            esac

            if test -z "$head"; then
                detached=yes
                b="$(
                case "${GIT_PS1_DESCRIBE_STYLE-}" in
                (contains)
                    git describe --contains HEAD ;;
                (branch)
                    git describe --contains --all HEAD ;;
                (tag)
                    git describe --tags HEAD ;;
                (describe)
                    git describe HEAD ;;
                (* | default)
                    git describe --tags --exact-match HEAD ;;
                esac 2>/dev/null)" ||

                b="$short_sha..."
                b="($b)"
            else
                b="$head"
            fi
        fi
    fi

    if [ -n "$step" ] && [ -n "$total" ]; then
        r="$r <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>t</mi><mi>e</mi><mi>p</mi><mi mathvariant="normal">/</mi></mrow><annotation encoding="application/x-tex">step/</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mord mathnormal">p</span><span class="mord">/</span></span></span></span>total"
    fi

    local conflict="" # state indicator for unresolved conflicts
    if [ "${GIT_PS1_SHOWCONFLICTSTATE-}" = "yes" ] &&
       [ "$(git ls-files --unmerged 2>/dev/null)" ]; then
        conflict="|CONFLICT"
    fi

    local w=""
    local i=""
    local s=""
    local u=""
    local h=""
    local c=""
    local p="" # short version of upstream state indicator
    local upstream="" # verbose version of upstream state indicator

    if [ "true" = "$inside_gitdir" ]; then
        if [ "true" = "$bare_repo" ]; then
            c="BARE:"
        else
            b="GIT_DIR!"
        fi
    elif [ "true" = "$inside_worktree" ]; then
        if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
           [ "$(git config --bool bash.showDirtyState)" != "false" ]
        then
            git diff --no-ext-diff --quiet || w="*"
            git diff --no-ext-diff --cached --quiet || i="+"
            if [ -z "$short_sha" ] && [ -z "$i" ]; then
                i="#"
            fi
        fi
        if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
           git rev-parse --verify --quiet refs/stash >/dev/null
        then
            s="$"
        fi

        if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
           [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
           git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null
        then
            u="%${ZSH_VERSION+%}"
        fi

        if [ -n "${GIT_PS1_COMPRESSSPARSESTATE-}" ] &&
           [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
            h="?"
        fi

        if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
            __git_ps1_show_upstream
        fi
    fi

    local z="${GIT_PS1_STATESEPARATOR- }"

    b=${b##refs/heads/}
    if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
        __git_ps1_branch_name=$b
        b="\${__git_ps1_branch_name}"
    fi

    if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
        __git_ps1_colorize_gitstring
    fi

    local f="$h$w$i$s$u$p"
    local gitstring="$c$b${f:+$z$f}${sparse}$r${upstream}${conflict}"

    if [ "$pcmode" = yes ]; then
        if [ "${__git_printf_supports_v-}" != yes ]; then
            gitstring=$(printf -- "$printf_format" "$gitstring")
        else
            printf -v gitstring -- "$printf_format" "$gitstring"
        fi
        PS1="$ps1pc_start$gitstring$ps1pc_end"
    else
        printf -- "$printf_format" "$gitstring"
    fi

    return "$exit"
}