Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 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 | # |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 6 | # This script allows you to see repository status in your prompt. |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 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 |
Junio C Hamano | de29a7a | 2012-12-11 15:04:36 -0800 | [diff] [blame] | 13 | # 3a) Change your PS1 to call __git_ps1 as |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 14 | # command-substitution: |
| 15 | # Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 16 | # ZSH: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ ' |
Junio C Hamano | de29a7a | 2012-12-11 15:04:36 -0800 | [diff] [blame] | 17 | # the optional argument will be used as format string. |
Junio C Hamano | 46b0459 | 2013-07-01 12:41:55 -0700 | [diff] [blame] | 18 | # 3b) Alternatively, for a slightly faster prompt, __git_ps1 can |
| 19 | # be used for PROMPT_COMMAND in Bash or for precmd() in Zsh |
SZEDER Gábor | a694258 | 2013-06-24 02:28:02 +0200 | [diff] [blame] | 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. |
Ramkumar Ramachandra | 9678696 | 2013-05-17 14:25:48 +0530 | [diff] [blame] | 23 | # Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "' |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 24 | # will show username, at-sign, host, colon, cwd, then |
| 25 | # various status string, followed by dollar and SP, as |
| 26 | # your prompt. |
Ramkumar Ramachandra | 9678696 | 2013-05-17 14:25:48 +0530 | [diff] [blame] | 27 | # ZSH: precmd () { __git_ps1 "%n" ":%~$ " "|%s" } |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 28 | # will show username, pipe, then various status string, |
| 29 | # followed by colon, cwd, dollar and SP, as your prompt. |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 30 | # Optionally, you can supply a third argument with a printf |
| 31 | # format string to finetune the output of the branch status |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 32 | # |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 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. |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 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 |
Martin Erik Werner | 66cb5d4 | 2013-02-13 12:01:58 +0100 | [diff] [blame] | 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. |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 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, ">" |
Jonathan "Duke" Leto | f9db192 | 2012-09-24 10:41:26 -0700 | [diff] [blame] | 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: |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 61 | # |
| 62 | # verbose show number of commits ahead/behind (+/-) upstream |
Julien Carsique | 1f6806c | 2013-10-10 16:40:39 +0200 | [diff] [blame] | 63 | # name if verbose, then also show the upstream abbrev name |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 64 | # legacy don't use the '--count' option available in recent |
| 65 | # versions of git-rev-list |
| 66 | # git always compare HEAD to @{upstream} |
| 67 | # svn always compare HEAD to your SVN upstream |
| 68 | # |
Joe Cridge | ab7fade | 2015-06-10 18:19:24 +0100 | [diff] [blame] | 69 | # You can change the separator between the branch name and the above |
| 70 | # state symbols by setting GIT_PS1_STATESEPARATOR. The default separator |
| 71 | # is SP. |
| 72 | # |
Elijah Newren | 30b00f0 | 2020-06-21 05:21:26 +0000 | [diff] [blame] | 73 | # When there is an in-progress operation such as a merge, rebase, |
| 74 | # revert, cherry-pick, or bisect, the prompt will include information |
| 75 | # related to the operation, often in the form "|<OPERATION-NAME>". |
| 76 | # |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 77 | # When the repository has a sparse-checkout, a notification of the form |
| 78 | # "|SPARSE" will be included in the prompt. This can be shortened to a |
| 79 | # single '?' character by setting GIT_PS1_COMPRESSSPARSESTATE, or omitted |
| 80 | # by setting GIT_PS1_OMITSPARSESTATE. |
| 81 | # |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 82 | # By default, __git_ps1 will compare HEAD to your SVN upstream if it can |
| 83 | # find one, or @{upstream} otherwise. Once you have set |
| 84 | # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by |
| 85 | # setting the bash.showUpstream config variable. |
Simon Oosthoek | 9b7e776 | 2012-10-10 21:32:24 +0200 | [diff] [blame] | 86 | # |
Anders Kaseorg | 50b03b0 | 2012-12-11 18:20:24 -0500 | [diff] [blame] | 87 | # If you would like to see more information about the identity of |
| 88 | # commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE |
| 89 | # to one of these values: |
| 90 | # |
| 91 | # contains relative to newer annotated tag (v1.6.3.2~35) |
| 92 | # branch relative to newer tag or branch (master~4) |
| 93 | # describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f) |
Michael J Gruber | 9f0b4dd | 2017-03-15 14:15:09 +0100 | [diff] [blame] | 94 | # tag relative to any older tag (v1.6.3.1-13-gdd42c2f) |
Anders Kaseorg | 50b03b0 | 2012-12-11 18:20:24 -0500 | [diff] [blame] | 95 | # default exactly matching tag |
Junio C Hamano | f993e2e | 2012-12-11 15:50:10 -0800 | [diff] [blame] | 96 | # |
Simon Oosthoek | 9b7e776 | 2012-10-10 21:32:24 +0200 | [diff] [blame] | 97 | # If you would like a colored hint about the current dirty state, set |
Simon Oosthoek | 9b3aaf8 | 2012-10-16 21:34:05 +0200 | [diff] [blame] | 98 | # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on |
Eduardo R. D'Avila | cf4cac4 | 2013-06-26 00:05:17 -0300 | [diff] [blame] | 99 | # the colored output of "git status -sb" and are available only when |
Felipe Contreras | ea625a3 | 2020-10-27 20:06:50 -0600 | [diff] [blame] | 100 | # using __git_ps1 for PROMPT_COMMAND or precmd in Bash, |
| 101 | # but always available in Zsh. |
Jess Austin | 0120b8c | 2015-01-06 20:22:27 -0500 | [diff] [blame] | 102 | # |
| 103 | # If you would like __git_ps1 to do nothing in the case when the current |
| 104 | # directory is set up to be ignored by git, then set |
| 105 | # GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the |
| 106 | # repository level by setting bash.hideIfPwdIgnored to "false". |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 107 | |
Brandon Casey | a44aa69 | 2013-08-21 18:39:03 -0700 | [diff] [blame] | 108 | # check whether printf supports -v |
| 109 | __git_printf_supports_v= |
| 110 | printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1 |
| 111 | |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 112 | # stores the divergence from upstream in $p |
| 113 | # used by GIT_PS1_SHOWUPSTREAM |
| 114 | __git_ps1_show_upstream () |
| 115 | { |
| 116 | local key value |
| 117 | local svn_remote svn_url_pattern count n |
Julien Carsique | 1f6806c | 2013-10-10 16:40:39 +0200 | [diff] [blame] | 118 | local upstream=git legacy="" verbose="" name="" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 119 | |
| 120 | svn_remote=() |
| 121 | # get some config options from git-config |
| 122 | local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')" |
| 123 | while read -r key value; do |
| 124 | case "$key" in |
| 125 | bash.showupstream) |
| 126 | GIT_PS1_SHOWUPSTREAM="$value" |
| 127 | if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then |
| 128 | p="" |
| 129 | return |
| 130 | fi |
| 131 | ;; |
| 132 | svn-remote.*.url) |
Thomas Gummerer | d0583da | 2013-05-22 09:40:39 +0200 | [diff] [blame] | 133 | svn_remote[$((${#svn_remote[@]} + 1))]="$value" |
SZEDER Gábor | 52ec889 | 2013-10-15 14:21:11 +0200 | [diff] [blame] | 134 | svn_url_pattern="$svn_url_pattern\\|$value" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 135 | upstream=svn+git # default upstream is SVN if available, else git |
| 136 | ;; |
| 137 | esac |
| 138 | done <<< "$output" |
| 139 | |
| 140 | # parse configuration values |
Sibo Dong | 9542d56 | 2020-10-31 22:09:46 +0000 | [diff] [blame] | 141 | local option |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 142 | for option in ${GIT_PS1_SHOWUPSTREAM}; do |
| 143 | case "$option" in |
| 144 | git|svn) upstream="$option" ;; |
| 145 | verbose) verbose=1 ;; |
| 146 | legacy) legacy=1 ;; |
Julien Carsique | 1f6806c | 2013-10-10 16:40:39 +0200 | [diff] [blame] | 147 | name) name=1 ;; |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 148 | esac |
| 149 | done |
| 150 | |
| 151 | # Find our upstream |
| 152 | case "$upstream" in |
| 153 | git) upstream="@{upstream}" ;; |
| 154 | svn*) |
| 155 | # get the upstream from the "git-svn-id: ..." in a commit message |
| 156 | # (git-svn uses essentially the same procedure internally) |
Thomas Gummerer | d0583da | 2013-05-22 09:40:39 +0200 | [diff] [blame] | 157 | local -a svn_upstream |
| 158 | svn_upstream=($(git log --first-parent -1 \ |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 159 | --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null)) |
| 160 | if [[ 0 -ne ${#svn_upstream[@]} ]]; then |
Thomas Gummerer | d0583da | 2013-05-22 09:40:39 +0200 | [diff] [blame] | 161 | svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]} |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 162 | svn_upstream=${svn_upstream%@*} |
| 163 | local n_stop="${#svn_remote[@]}" |
| 164 | for ((n=1; n <= n_stop; n++)); do |
| 165 | svn_upstream=${svn_upstream#${svn_remote[$n]}} |
| 166 | done |
| 167 | |
| 168 | if [[ -z "$svn_upstream" ]]; then |
| 169 | # default branch name for checkouts with no layout: |
| 170 | upstream=${GIT_SVN_ID:-git-svn} |
| 171 | else |
| 172 | upstream=${svn_upstream#/} |
| 173 | fi |
| 174 | elif [[ "svn+git" = "$upstream" ]]; then |
| 175 | upstream="@{upstream}" |
| 176 | fi |
| 177 | ;; |
| 178 | esac |
| 179 | |
| 180 | # Find how many commits we are ahead/behind our upstream |
| 181 | if [[ -z "$legacy" ]]; then |
| 182 | count="$(git rev-list --count --left-right \ |
| 183 | "$upstream"...HEAD 2>/dev/null)" |
| 184 | else |
| 185 | # produce equivalent output to --count for older versions of git |
| 186 | local commits |
| 187 | if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)" |
| 188 | then |
| 189 | local commit behind=0 ahead=0 |
| 190 | for commit in $commits |
| 191 | do |
| 192 | case "$commit" in |
| 193 | "<"*) ((behind++)) ;; |
| 194 | *) ((ahead++)) ;; |
| 195 | esac |
| 196 | done |
| 197 | count="$behind $ahead" |
| 198 | else |
| 199 | count="" |
| 200 | fi |
| 201 | fi |
| 202 | |
| 203 | # calculate the result |
| 204 | if [[ -z "$verbose" ]]; then |
| 205 | case "$count" in |
| 206 | "") # no upstream |
| 207 | p="" ;; |
| 208 | "0 0") # equal to upstream |
| 209 | p="=" ;; |
| 210 | "0 "*) # ahead of upstream |
| 211 | p=">" ;; |
| 212 | *" 0") # behind upstream |
| 213 | p="<" ;; |
| 214 | *) # diverged from upstream |
| 215 | p="<>" ;; |
| 216 | esac |
| 217 | else |
| 218 | case "$count" in |
| 219 | "") # no upstream |
| 220 | p="" ;; |
| 221 | "0 0") # equal to upstream |
| 222 | p=" u=" ;; |
| 223 | "0 "*) # ahead of upstream |
| 224 | p=" u+${count#0 }" ;; |
| 225 | *" 0") # behind upstream |
| 226 | p=" u-${count% 0}" ;; |
| 227 | *) # diverged from upstream |
| 228 | p=" u+${count#* }-${count% *}" ;; |
| 229 | esac |
Julien Carsique | 1f6806c | 2013-10-10 16:40:39 +0200 | [diff] [blame] | 230 | if [[ -n "$count" && -n "$name" ]]; then |
Richard Hansen | 8976500 | 2014-04-21 19:53:09 -0400 | [diff] [blame] | 231 | __git_ps1_upstream_name=$(git rev-parse \ |
| 232 | --abbrev-ref "$upstream" 2>/dev/null) |
Richard Hansen | 1e4119c | 2014-05-19 18:55:37 -0400 | [diff] [blame] | 233 | if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then |
Richard Hansen | 8976500 | 2014-04-21 19:53:09 -0400 | [diff] [blame] | 234 | p="$p \${__git_ps1_upstream_name}" |
| 235 | else |
| 236 | p="$p ${__git_ps1_upstream_name}" |
| 237 | # not needed anymore; keep user's |
| 238 | # environment clean |
| 239 | unset __git_ps1_upstream_name |
| 240 | fi |
Julien Carsique | 1f6806c | 2013-10-10 16:40:39 +0200 | [diff] [blame] | 241 | fi |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 242 | fi |
| 243 | |
| 244 | } |
| 245 | |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 246 | # Helper function that is meant to be called from __git_ps1. It |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 247 | # injects color codes into the appropriate gitstring variables used |
| 248 | # to build a gitstring. |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 249 | __git_ps1_colorize_gitstring () |
| 250 | { |
Ramkumar Ramachandra | 9678696 | 2013-05-17 14:25:48 +0530 | [diff] [blame] | 251 | if [[ -n ${ZSH_VERSION-} ]]; then |
| 252 | local c_red='%F{red}' |
| 253 | local c_green='%F{green}' |
| 254 | local c_lblue='%F{blue}' |
| 255 | local c_clear='%f' |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 256 | else |
| 257 | # Using \[ and \] around colors is necessary to prevent |
| 258 | # issues with command line editing/browsing/completion! |
| 259 | local c_red='\[\e[31m\]' |
| 260 | local c_green='\[\e[32m\]' |
| 261 | local c_lblue='\[\e[1;34m\]' |
| 262 | local c_clear='\[\e[0m\]' |
Ramkumar Ramachandra | 9678696 | 2013-05-17 14:25:48 +0530 | [diff] [blame] | 263 | fi |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 264 | local bad_color=$c_red |
| 265 | local ok_color=$c_green |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 266 | local flags_color="$c_lblue" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 267 | |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 268 | local branch_color="" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 269 | if [ $detached = no ]; then |
| 270 | branch_color="$ok_color" |
| 271 | else |
| 272 | branch_color="$bad_color" |
| 273 | fi |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 274 | c="$branch_color$c" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 275 | |
Eduardo R. D'Avila | 15981f4 | 2013-06-26 00:05:16 -0300 | [diff] [blame] | 276 | z="$c_clear$z" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 277 | if [ "$w" = "*" ]; then |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 278 | w="$bad_color$w" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 279 | fi |
| 280 | if [ -n "$i" ]; then |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 281 | i="$ok_color$i" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 282 | fi |
| 283 | if [ -n "$s" ]; then |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 284 | s="$flags_color$s" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 285 | fi |
| 286 | if [ -n "$u" ]; then |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 287 | u="$bad_color$u" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 288 | fi |
Eduardo R. D'Avila | 7fe9031 | 2013-06-26 00:05:14 -0300 | [diff] [blame] | 289 | r="$c_clear$r" |
Ramkumar Ramachandra | 18562ad | 2013-05-17 14:25:47 +0530 | [diff] [blame] | 290 | } |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 291 | |
Robert Abel | 5501f50 | 2017-12-06 00:39:11 +0100 | [diff] [blame] | 292 | # Helper function to read the first line of a file into a variable. |
| 293 | # __git_eread requires 2 arguments, the file path and the name of the |
| 294 | # variable, in that order. |
Felipe Contreras | 66ab301 | 2014-05-13 08:21:19 -0500 | [diff] [blame] | 295 | __git_eread () |
Felipe Contreras | 59d3924 | 2014-04-11 18:32:25 -0500 | [diff] [blame] | 296 | { |
Robert Abel | 041fe8f | 2017-12-06 00:39:12 +0100 | [diff] [blame] | 297 | test -r "$1" && IFS=$'\r\n' read "$2" <"$1" |
Felipe Contreras | 59d3924 | 2014-04-11 18:32:25 -0500 | [diff] [blame] | 298 | } |
| 299 | |
Phillip Wood | e981bf7 | 2019-07-01 07:21:06 -0700 | [diff] [blame] | 300 | # see if a cherry-pick or revert is in progress, if the user has committed a |
| 301 | # conflict resolution with 'git commit' in the middle of a sequence of picks or |
| 302 | # reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read |
| 303 | # the todo file. |
| 304 | __git_sequencer_status () |
| 305 | { |
| 306 | local todo |
| 307 | if test -f "$g/CHERRY_PICK_HEAD" |
| 308 | then |
| 309 | r="|CHERRY-PICKING" |
| 310 | return 0; |
| 311 | elif test -f "$g/REVERT_HEAD" |
| 312 | then |
| 313 | r="|REVERTING" |
| 314 | return 0; |
| 315 | elif __git_eread "$g/sequencer/todo" todo |
| 316 | then |
| 317 | case "$todo" in |
| 318 | p[\ \ ]|pick[\ \ ]*) |
| 319 | r="|CHERRY-PICKING" |
| 320 | return 0 |
| 321 | ;; |
| 322 | revert[\ \ ]*) |
| 323 | r="|REVERTING" |
| 324 | return 0 |
| 325 | ;; |
| 326 | esac |
| 327 | fi |
| 328 | return 1 |
| 329 | } |
| 330 | |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 331 | # __git_ps1 accepts 0 or 1 arguments (i.e., format string) |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 332 | # when called from PS1 using command substitution |
| 333 | # in this mode it prints text to add to bash PS1 prompt (includes branch name) |
| 334 | # |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 335 | # __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc) |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 336 | # in that case it _sets_ PS1. The arguments are parts of a PS1 string. |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 337 | # when two arguments are given, the first is prepended and the second appended |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 338 | # to the state string when assigned to PS1. |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 339 | # The optional third parameter will be used as printf format string to further |
| 340 | # customize the output of the git-status string. |
Simon Oosthoek | 9b7e776 | 2012-10-10 21:32:24 +0200 | [diff] [blame] | 341 | # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 342 | __git_ps1 () |
| 343 | { |
Tony Finch | 6babe76 | 2015-01-14 10:06:28 +0000 | [diff] [blame] | 344 | # preserve exit status |
Tony Finch | eb443e3 | 2014-12-22 18:09:25 +0000 | [diff] [blame] | 345 | local exit=$? |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 346 | local pcmode=no |
Simon Oosthoek | 9b3aaf8 | 2012-10-16 21:34:05 +0200 | [diff] [blame] | 347 | local detached=no |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 348 | local ps1pc_start='\u@\h:\w ' |
| 349 | local ps1pc_end='\$ ' |
| 350 | local printf_format=' (%s)' |
| 351 | |
| 352 | case "$#" in |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 353 | 2|3) pcmode=yes |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 354 | ps1pc_start="$1" |
| 355 | ps1pc_end="$2" |
Simon Oosthoek | 126b596 | 2012-12-26 20:15:05 +0100 | [diff] [blame] | 356 | printf_format="${3:-$printf_format}" |
Richard Hansen | 76b4309 | 2015-01-06 20:22:26 -0500 | [diff] [blame] | 357 | # set PS1 to a plain prompt so that we can |
| 358 | # simply return early if the prompt should not |
| 359 | # be decorated |
| 360 | PS1="$ps1pc_start$ps1pc_end" |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 361 | ;; |
| 362 | 0|1) printf_format="${1:-$printf_format}" |
| 363 | ;; |
Tony Finch | 6babe76 | 2015-01-14 10:06:28 +0000 | [diff] [blame] | 364 | *) return $exit |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 365 | ;; |
| 366 | esac |
| 367 | |
Richard Hansen | 1e4119c | 2014-05-19 18:55:37 -0400 | [diff] [blame] | 368 | # ps1_expanded: This variable is set to 'yes' if the shell |
| 369 | # subjects the value of PS1 to parameter expansion: |
| 370 | # |
| 371 | # * bash does unless the promptvars option is disabled |
| 372 | # * zsh does not unless the PROMPT_SUBST option is set |
| 373 | # * POSIX shells always do |
| 374 | # |
| 375 | # If the shell would expand the contents of PS1 when drawing |
| 376 | # the prompt, a raw ref name must not be included in PS1. |
| 377 | # This protects the user from arbitrary code execution via |
| 378 | # specially crafted ref names. For example, a ref named |
| 379 | # 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the |
| 380 | # shell to execute 'sudo rm -rf /' when the prompt is drawn. |
| 381 | # |
| 382 | # Instead, the ref name should be placed in a separate global |
| 383 | # variable (in the __git_ps1_* namespace to avoid colliding |
| 384 | # with the user's environment) and that variable should be |
| 385 | # referenced from PS1. For example: |
| 386 | # |
| 387 | # __git_ps1_foo=$(do_something_to_get_ref_name) |
| 388 | # PS1="...stuff...\${__git_ps1_foo}...stuff..." |
| 389 | # |
| 390 | # If the shell does not expand the contents of PS1, the raw |
| 391 | # ref name must be included in PS1. |
| 392 | # |
| 393 | # The value of this variable is only relevant when in pcmode. |
| 394 | # |
| 395 | # Assume that the shell follows the POSIX specification and |
| 396 | # expands PS1 unless determined otherwise. (This is more |
| 397 | # likely to be correct if the user has a non-bash, non-zsh |
| 398 | # shell and safer than the alternative if the assumption is |
| 399 | # incorrect.) |
| 400 | # |
| 401 | local ps1_expanded=yes |
Ville Skyttä | 34d8f5a | 2016-06-06 19:29:33 +0300 | [diff] [blame] | 402 | [ -z "${ZSH_VERSION-}" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no |
| 403 | [ -z "${BASH_VERSION-}" ] || shopt -q promptvars || ps1_expanded=no |
Richard Hansen | 1e4119c | 2014-05-19 18:55:37 -0400 | [diff] [blame] | 404 | |
SZEDER Gábor | e3e0b93 | 2013-06-24 02:16:02 +0200 | [diff] [blame] | 405 | local repo_info rev_parse_exit_code |
| 406 | repo_info="$(git rev-parse --git-dir --is-inside-git-dir \ |
| 407 | --is-bare-repository --is-inside-work-tree \ |
| 408 | --short HEAD 2>/dev/null)" |
| 409 | rev_parse_exit_code="$?" |
| 410 | |
SZEDER Gábor | efaa0c1 | 2013-06-17 22:58:42 +0200 | [diff] [blame] | 411 | if [ -z "$repo_info" ]; then |
Tony Finch | 6babe76 | 2015-01-14 10:06:28 +0000 | [diff] [blame] | 412 | return $exit |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 413 | fi |
| 414 | |
Ville Skyttä | 34d8f5a | 2016-06-06 19:29:33 +0300 | [diff] [blame] | 415 | local short_sha="" |
SZEDER Gábor | e3e0b93 | 2013-06-24 02:16:02 +0200 | [diff] [blame] | 416 | if [ "$rev_parse_exit_code" = "0" ]; then |
| 417 | short_sha="${repo_info##*$'\n'}" |
| 418 | repo_info="${repo_info%$'\n'*}" |
| 419 | fi |
SZEDER Gábor | efaa0c1 | 2013-06-17 22:58:42 +0200 | [diff] [blame] | 420 | local inside_worktree="${repo_info##*$'\n'}" |
| 421 | repo_info="${repo_info%$'\n'*}" |
| 422 | local bare_repo="${repo_info##*$'\n'}" |
| 423 | repo_info="${repo_info%$'\n'*}" |
| 424 | local inside_gitdir="${repo_info##*$'\n'}" |
| 425 | local g="${repo_info%$'\n'*}" |
| 426 | |
Jess Austin | 0120b8c | 2015-01-06 20:22:27 -0500 | [diff] [blame] | 427 | if [ "true" = "$inside_worktree" ] && |
| 428 | [ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] && |
| 429 | [ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] && |
| 430 | git check-ignore -q . |
| 431 | then |
Junio C Hamano | 9920c71 | 2015-01-14 12:35:48 -0800 | [diff] [blame] | 432 | return $exit |
Jess Austin | 0120b8c | 2015-01-06 20:22:27 -0500 | [diff] [blame] | 433 | fi |
| 434 | |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 435 | local sparse="" |
Elijah Newren | 5c0cbdb | 2021-05-13 06:22:36 +0000 | [diff] [blame] | 436 | if [ -z "${GIT_PS1_COMPRESSSPARSESTATE-}" ] && |
| 437 | [ -z "${GIT_PS1_OMITSPARSESTATE-}" ] && |
David J. Malan | e8882a8 | 2020-07-21 00:15:31 +0000 | [diff] [blame] | 438 | [ "$(git config --bool core.sparseCheckout)" = "true" ]; then |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 439 | sparse="|SPARSE" |
| 440 | fi |
| 441 | |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 442 | local r="" |
| 443 | local b="" |
| 444 | local step="" |
| 445 | local total="" |
| 446 | if [ -d "$g/rebase-merge" ]; then |
Felipe Contreras | 66ab301 | 2014-05-13 08:21:19 -0500 | [diff] [blame] | 447 | __git_eread "$g/rebase-merge/head-name" b |
| 448 | __git_eread "$g/rebase-merge/msgnum" step |
| 449 | __git_eread "$g/rebase-merge/end" total |
Elijah Newren | 6d04ce7 | 2020-02-15 21:36:35 +0000 | [diff] [blame] | 450 | r="|REBASE" |
Simon Oosthoek | 1bfc51a | 2012-10-10 21:31:58 +0200 | [diff] [blame] | 451 | else |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 452 | if [ -d "$g/rebase-apply" ]; then |
Felipe Contreras | 66ab301 | 2014-05-13 08:21:19 -0500 | [diff] [blame] | 453 | __git_eread "$g/rebase-apply/next" step |
| 454 | __git_eread "$g/rebase-apply/last" total |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 455 | if [ -f "$g/rebase-apply/rebasing" ]; then |
Felipe Contreras | 66ab301 | 2014-05-13 08:21:19 -0500 | [diff] [blame] | 456 | __git_eread "$g/rebase-apply/head-name" b |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 457 | r="|REBASE" |
| 458 | elif [ -f "$g/rebase-apply/applying" ]; then |
| 459 | r="|AM" |
Zoltan Klinger | b71dc3e | 2013-04-25 19:28:54 +1000 | [diff] [blame] | 460 | else |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 461 | r="|AM/REBASE" |
Zoltan Klinger | b71dc3e | 2013-04-25 19:28:54 +1000 | [diff] [blame] | 462 | fi |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 463 | elif [ -f "$g/MERGE_HEAD" ]; then |
| 464 | r="|MERGING" |
Phillip Wood | e981bf7 | 2019-07-01 07:21:06 -0700 | [diff] [blame] | 465 | elif __git_sequencer_status; then |
| 466 | : |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 467 | elif [ -f "$g/BISECT_LOG" ]; then |
| 468 | r="|BISECTING" |
| 469 | fi |
| 470 | |
SZEDER Gábor | 3a43c4b | 2011-03-31 23:41:18 +0200 | [diff] [blame] | 471 | if [ -n "$b" ]; then |
| 472 | : |
| 473 | elif [ -h "$g/HEAD" ]; then |
| 474 | # symlink symbolic ref |
| 475 | b="$(git symbolic-ref HEAD 2>/dev/null)" |
| 476 | else |
| 477 | local head="" |
Felipe Contreras | 66ab301 | 2014-05-13 08:21:19 -0500 | [diff] [blame] | 478 | if ! __git_eread "$g/HEAD" head; then |
Tony Finch | 6babe76 | 2015-01-14 10:06:28 +0000 | [diff] [blame] | 479 | return $exit |
SZEDER Gábor | 3a43c4b | 2011-03-31 23:41:18 +0200 | [diff] [blame] | 480 | fi |
| 481 | # is it a symbolic ref? |
| 482 | b="${head#ref: }" |
| 483 | if [ "$head" = "$b" ]; then |
| 484 | detached=yes |
| 485 | b="$( |
| 486 | case "${GIT_PS1_DESCRIBE_STYLE-}" in |
| 487 | (contains) |
| 488 | git describe --contains HEAD ;; |
| 489 | (branch) |
| 490 | git describe --contains --all HEAD ;; |
Michael J Gruber | 9f0b4dd | 2017-03-15 14:15:09 +0100 | [diff] [blame] | 491 | (tag) |
| 492 | git describe --tags HEAD ;; |
SZEDER Gábor | 3a43c4b | 2011-03-31 23:41:18 +0200 | [diff] [blame] | 493 | (describe) |
| 494 | git describe HEAD ;; |
| 495 | (* | default) |
| 496 | git describe --tags --exact-match HEAD ;; |
| 497 | esac 2>/dev/null)" || |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 498 | |
SZEDER Gábor | e3e0b93 | 2013-06-24 02:16:02 +0200 | [diff] [blame] | 499 | b="$short_sha..." |
SZEDER Gábor | 3a43c4b | 2011-03-31 23:41:18 +0200 | [diff] [blame] | 500 | b="($b)" |
| 501 | fi |
| 502 | fi |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 503 | fi |
| 504 | |
| 505 | if [ -n "$step" ] && [ -n "$total" ]; then |
| 506 | r="$r $step/$total" |
| 507 | fi |
| 508 | |
| 509 | local w="" |
| 510 | local i="" |
| 511 | local s="" |
| 512 | local u="" |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 513 | local h="" |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 514 | local c="" |
| 515 | local p="" |
| 516 | |
SZEDER Gábor | efaa0c1 | 2013-06-17 22:58:42 +0200 | [diff] [blame] | 517 | if [ "true" = "$inside_gitdir" ]; then |
| 518 | if [ "true" = "$bare_repo" ]; then |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 519 | c="BARE:" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 520 | else |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 521 | b="GIT_DIR!" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 522 | fi |
SZEDER Gábor | efaa0c1 | 2013-06-17 22:58:42 +0200 | [diff] [blame] | 523 | elif [ "true" = "$inside_worktree" ]; then |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 524 | if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] && |
| 525 | [ "$(git config --bool bash.showDirtyState)" != "false" ] |
| 526 | then |
SZEDER Gábor | 0af9f7e | 2015-11-21 15:46:40 +0100 | [diff] [blame] | 527 | git diff --no-ext-diff --quiet || w="*" |
SZEDER Gábor | c26f70c | 2015-11-21 12:30:09 +0100 | [diff] [blame] | 528 | git diff --no-ext-diff --cached --quiet || i="+" |
| 529 | if [ -z "$short_sha" ] && [ -z "$i" ]; then |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 530 | i="#" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 531 | fi |
| 532 | fi |
SZEDER Gábor | dd0b72c | 2011-04-01 17:47:37 +0200 | [diff] [blame] | 533 | if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] && |
Jeff King | 0fa7f01 | 2014-08-23 01:26:51 -0400 | [diff] [blame] | 534 | git rev-parse --verify --quiet refs/stash >/dev/null |
| 535 | then |
SZEDER Gábor | dd0b72c | 2011-04-01 17:47:37 +0200 | [diff] [blame] | 536 | s="$" |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 537 | fi |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 538 | |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 539 | if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && |
| 540 | [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && |
SZEDER Gábor | dd160d7 | 2015-07-19 13:28:06 +0200 | [diff] [blame] | 541 | git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 542 | then |
| 543 | u="%${ZSH_VERSION+%}" |
| 544 | fi |
| 545 | |
Elijah Newren | 5c0cbdb | 2021-05-13 06:22:36 +0000 | [diff] [blame] | 546 | if [ -n "${GIT_PS1_COMPRESSSPARSESTATE-}" ] && |
David J. Malan | e8882a8 | 2020-07-21 00:15:31 +0000 | [diff] [blame] | 547 | [ "$(git config --bool core.sparseCheckout)" = "true" ]; then |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 548 | h="?" |
| 549 | fi |
| 550 | |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 551 | if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then |
| 552 | __git_ps1_show_upstream |
| 553 | fi |
| 554 | fi |
| 555 | |
| 556 | local z="${GIT_PS1_STATESEPARATOR-" "}" |
Junio C Hamano | 46b0459 | 2013-07-01 12:41:55 -0700 | [diff] [blame] | 557 | |
Felipe Contreras | ea625a3 | 2020-10-27 20:06:50 -0600 | [diff] [blame] | 558 | # NO color option unless in PROMPT_COMMAND mode or it's Zsh |
| 559 | if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then |
| 560 | if [ $pcmode = yes ] || [ -n "${ZSH_VERSION-}" ]; then |
| 561 | __git_ps1_colorize_gitstring |
| 562 | fi |
Junio C Hamano | 46b0459 | 2013-07-01 12:41:55 -0700 | [diff] [blame] | 563 | fi |
| 564 | |
Richard Hansen | 8976500 | 2014-04-21 19:53:09 -0400 | [diff] [blame] | 565 | b=${b##refs/heads/} |
Richard Hansen | 1e4119c | 2014-05-19 18:55:37 -0400 | [diff] [blame] | 566 | if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then |
Richard Hansen | 8976500 | 2014-04-21 19:53:09 -0400 | [diff] [blame] | 567 | __git_ps1_branch_name=$b |
Richard Hansen | 8976500 | 2014-04-21 19:53:09 -0400 | [diff] [blame] | 568 | b="\${__git_ps1_branch_name}" |
| 569 | fi |
| 570 | |
Elijah Newren | afda36d | 2020-06-21 05:21:27 +0000 | [diff] [blame] | 571 | local f="$h$w$i$s$u" |
| 572 | local gitstring="$c$b${f:+$z$f}${sparse}$r$p" |
Junio C Hamano | 46b0459 | 2013-07-01 12:41:55 -0700 | [diff] [blame] | 573 | |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 574 | if [ $pcmode = yes ]; then |
Brandon Casey | a44aa69 | 2013-08-21 18:39:03 -0700 | [diff] [blame] | 575 | if [ "${__git_printf_supports_v-}" != yes ]; then |
SZEDER Gábor | 69a8141 | 2013-06-17 21:42:55 +0200 | [diff] [blame] | 576 | gitstring=$(printf -- "$printf_format" "$gitstring") |
| 577 | else |
| 578 | printf -v gitstring -- "$printf_format" "$gitstring" |
| 579 | fi |
SZEDER Gábor | 96ea404 | 2011-09-05 20:53:37 +0200 | [diff] [blame] | 580 | PS1="$ps1pc_start$gitstring$ps1pc_end" |
| 581 | else |
Junio C Hamano | 46b0459 | 2013-07-01 12:41:55 -0700 | [diff] [blame] | 582 | printf -- "$printf_format" "$gitstring" |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 583 | fi |
Tony Finch | eb443e3 | 2014-12-22 18:09:25 +0000 | [diff] [blame] | 584 | |
Tony Finch | eb443e3 | 2014-12-22 18:09:25 +0000 | [diff] [blame] | 585 | return $exit |
Felipe Contreras | af31a45 | 2012-05-22 22:46:40 +0200 | [diff] [blame] | 586 | } |