Skip to content

Commit 6d158cb

Browse files
Andrew Sayersgitster
authored andcommitted
bash completion: Support "divergence from upstream" messages in __git_ps1
Add a notification in the command prompt specifying whether (and optionally how far) your branch has diverged from its upstream. This is especially helpful in small teams that very frequently (forget to) push to each other. Support git-svn upstream detection as a special case, as migrators from centralised version control systems are especially likely to forget to push. Support for other types of upstream than SVN should be easy to add if anyone is so inclined. Signed-off-by: Andrew Sayers <[email protected]> Acked-by: Shawn O. Pearce <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f69c501 commit 6d158cb

File tree

1 file changed

+143
-1
lines changed

1 file changed

+143
-1
lines changed

contrib/completion/git-completion.bash

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@
4242
# set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
4343
# untracked files, then a '%' will be shown next to the branch name.
4444
#
45+
# If you would like to see the difference between HEAD and its
46+
# upstream, set GIT_PS1_SHOWUPSTREAM="auto". A "<" indicates
47+
# you are behind, ">" indicates you are ahead, and "<>"
48+
# indicates you have diverged. You can further control
49+
# behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated
50+
# list of values:
51+
# verbose show number of commits ahead/behind (+/-) upstream
52+
# legacy don't use the '--count' option available in recent
53+
# versions of git-rev-list
54+
# git always compare HEAD to @{upstream}
55+
# svn always compare HEAD to your SVN upstream
56+
# By default, __git_ps1 will compare HEAD to your SVN upstream
57+
# if it can find one, or @{upstream} otherwise. Once you have
58+
# set GIT_PS1_SHOWUPSTREAM, you can override it on a
59+
# per-repository basis by setting the bash.showUpstream config
60+
# variable.
61+
#
62+
#
4563
# To submit patches:
4664
#
4765
# *) Read Documentation/SubmittingPatches
@@ -78,6 +96,125 @@ __gitdir ()
7896
fi
7997
}
8098

99+
# stores the divergence from upstream in $p
100+
# used by GIT_PS1_SHOWUPSTREAM
101+
__git_ps1_show_upstream ()
102+
{
103+
local key value
104+
local svn_remote=() svn_url_pattern count n
105+
local upstream=git legacy="" verbose=""
106+
107+
# get some config options from git-config
108+
while read key value; do
109+
case "$key" in
110+
bash.showupstream)
111+
GIT_PS1_SHOWUPSTREAM="$value"
112+
if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
113+
p=""
114+
return
115+
fi
116+
;;
117+
svn-remote.*.url)
118+
svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
119+
svn_url_pattern+="\\|$value"
120+
upstream=svn+git # default upstream is SVN if available, else git
121+
;;
122+
esac
123+
done < <(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')
124+
125+
# parse configuration values
126+
for option in ${GIT_PS1_SHOWUPSTREAM}; do
127+
case "$option" in
128+
git|svn) upstream="$option" ;;
129+
verbose) verbose=1 ;;
130+
legacy) legacy=1 ;;
131+
esac
132+
done
133+
134+
# Find our upstream
135+
case "$upstream" in
136+
git) upstream="@{upstream}" ;;
137+
svn*)
138+
# get the upstream from the "git-svn-id: ..." in a commit message
139+
# (git-svn uses essentially the same procedure internally)
140+
local svn_upstream=($(git log --first-parent -1 \
141+
--grep="^git-svn-id: \(${svn_url_pattern:2}\)" 2>/dev/null))
142+
if [[ 0 -ne ${#svn_upstream[@]} ]]; then
143+
svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
144+
svn_upstream=${svn_upstream%@*}
145+
for ((n=1; "$n" <= "${#svn_remote[@]}"; ++n)); do
146+
svn_upstream=${svn_upstream#${svn_remote[$n]}}
147+
done
148+
149+
if [[ -z "$svn_upstream" ]]; then
150+
# default branch name for checkouts with no layout:
151+
upstream=${GIT_SVN_ID:-git-svn}
152+
else
153+
upstream=${svn_upstream#/}
154+
fi
155+
elif [[ "svn+git" = "$upstream" ]]; then
156+
upstream="@{upstream}"
157+
fi
158+
;;
159+
esac
160+
161+
# Find how many commits we are ahead/behind our upstream
162+
if [[ -z "$legacy" ]]; then
163+
count="$(git rev-list --count --left-right \
164+
"$upstream"...HEAD 2>/dev/null)"
165+
else
166+
# produce equivalent output to --count for older versions of git
167+
local commits
168+
if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
169+
then
170+
local commit behind=0 ahead=0
171+
for commit in $commits
172+
do
173+
case "$commit" in
174+
"<"*) let ++behind
175+
;;
176+
*) let ++ahead
177+
;;
178+
esac
179+
done
180+
count="$behind $ahead"
181+
else
182+
count=""
183+
fi
184+
fi
185+
186+
# calculate the result
187+
if [[ -z "$verbose" ]]; then
188+
case "$count" in
189+
"") # no upstream
190+
p="" ;;
191+
"0 0") # equal to upstream
192+
p="=" ;;
193+
"0 "*) # ahead of upstream
194+
p=">" ;;
195+
*" 0") # behind upstream
196+
p="<" ;;
197+
*) # diverged from upstream
198+
p="<>" ;;
199+
esac
200+
else
201+
case "$count" in
202+
"") # no upstream
203+
p="" ;;
204+
"0 0") # equal to upstream
205+
p=" u=" ;;
206+
"0 "*) # ahead of upstream
207+
p=" u+${count#0 }" ;;
208+
*" 0") # behind upstream
209+
p=" u-${count% 0}" ;;
210+
*) # diverged from upstream
211+
p=" u+${count#* }-${count% *}" ;;
212+
esac
213+
fi
214+
215+
}
216+
217+
81218
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
82219
# returns text to add to bash PS1 prompt (includes branch name)
83220
__git_ps1 ()
@@ -132,6 +269,7 @@ __git_ps1 ()
132269
local s
133270
local u
134271
local c
272+
local p=""
135273

136274
if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
137275
if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
@@ -159,10 +297,14 @@ __git_ps1 ()
159297
u="%"
160298
fi
161299
fi
300+
301+
if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
302+
__git_ps1_show_upstream
303+
fi
162304
fi
163305

164306
local f="$w$i$s$u"
165-
printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r"
307+
printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
166308
fi
167309
}
168310

0 commit comments

Comments
 (0)