Skip to content

Commit f2e264e

Browse files
avihgitster
authored andcommitted
git-prompt: don't use shell arrays
Arrays only existed in the svn-upstream code, used to: - Keep a list of svn remotes. - Convert commit msg to array of words, extract the 2nd-to-last word. Except bash/zsh, nearly all shells failed load on syntax errors here. Now: - The svn remotes are a list of newline-terminated values. - The 2nd-to-last word is extracted using standard shell substrings. - All shells can digest the svn-upstream code. While using shell field splitting to extract the word is simple, and doesn't even need non-standard code, e.g. set -- $(git log -1 ...), it would have the same issues as the old array code: it depends on IFS which we don't control, and it's subject to glob-expansion, e.g. if the message happens to include * or **/* (as this commit message just did), then the array could get huge. This was not great. Now it uses standard shell substrings, and we know the exact delimiter to expect, because it's the match from our grep just one line earlier. The new word extraction code also fixes svn-upstream in zsh, because previously it used arr[len-2], but because in zsh, unlike bash, array subscripts are 1-based, it incorrectly extracted the 3rd-to-last word. symptom: missing upstream status in a git-svn repo: u=, u+N-M, etc. The breakage in zsh is surprising, because it was last touched by commit d0583da (prompt: fix show upstream with svn and zsh), claiming to fix exactly that. However, it only mentions syntax fixes. It's unclear if behavior was fixed too. But it was broken, now fixed. Note LF=$'\n' and then using $LF instead of $'\n' few times. A future commit will add fallback for shells without $'...', so this would be the only line to touch instead of replacing every $'\n' . Shells which could run the previous array code: - bash Shells which have arrays but were broken anyway: - zsh: 1-based subscript - ksh93: no "local" (the new code can't fix this part...) - mksh, openbsd sh, pdksh: failed load on syntax error: "for ((...))". More shells which Failed to load due to syntax error: - dash, free/net bsd sh, busybox-ash, Schily Bourne shell, yash. Signed-off-by: Avi Halachmi (:avih) <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6df4b09 commit f2e264e

File tree

1 file changed

+30
-18
lines changed

1 file changed

+30
-18
lines changed

contrib/completion/git-prompt.sh

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
116116
__git_ps1_show_upstream ()
117117
{
118118
local key value
119-
local svn_remote svn_url_pattern="" count n
119+
local svn_remotes="" svn_url_pattern="" count n
120120
local upstream_type=git legacy="" verbose="" name=""
121+
local LF=$'\n'
121122

122-
svn_remote=()
123123
# get some config options from git-config
124124
local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
125125
while read -r key value; do
@@ -132,7 +132,7 @@ __git_ps1_show_upstream ()
132132
fi
133133
;;
134134
svn-remote.*.url)
135-
svn_remote[$((${#svn_remote[@]} + 1))]="$value"
135+
svn_remotes=${svn_remotes}${value}${LF} # URI\nURI\n...
136136
svn_url_pattern="$svn_url_pattern\\|$value"
137137
upstream_type=svn+git # default upstream type is SVN if available, else git
138138
;;
@@ -156,25 +156,37 @@ __git_ps1_show_upstream ()
156156
case "$upstream_type" in
157157
git) upstream_type="@{upstream}" ;;
158158
svn*)
159-
# get the upstream from the "git-svn-id: ..." in a commit message
160-
# (git-svn uses essentially the same procedure internally)
161-
local -a svn_upstream
162-
svn_upstream=($(git log --first-parent -1 \
163-
--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
164-
if [[ 0 -ne ${#svn_upstream[@]} ]]; then
165-
svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
166-
svn_upstream=${svn_upstream%@*}
167-
local n_stop="${#svn_remote[@]}"
168-
for ((n=1; n <= n_stop; n++)); do
169-
svn_upstream=${svn_upstream#${svn_remote[$n]}}
170-
done
159+
# successful svn-upstream resolution:
160+
# - get the list of configured svn-remotes ($svn_remotes set above)
161+
# - get the last commit which seems from one of our svn-remotes
162+
# - confirm that it is from one of the svn-remotes
163+
# - use $GIT_SVN_ID if set, else "git-svn"
171164

172-
if [[ -z "$svn_upstream" ]]; then
165+
# get upstream from "git-svn-id: UPSTRM@N HASH" in a commit message
166+
# (git-svn uses essentially the same procedure internally)
167+
local svn_upstream="$(
168+
git log --first-parent -1 \
169+
--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null
170+
)"
171+
172+
if [ -n "$svn_upstream" ]; then
173+
# extract the URI, assuming --grep matched the last line
174+
svn_upstream=${svn_upstream##*$LF} # last line
175+
svn_upstream=${svn_upstream#*: } # UPSTRM@N HASH
176+
svn_upstream=${svn_upstream%@*} # UPSTRM
177+
178+
case ${LF}${svn_remotes} in
179+
*"${LF}${svn_upstream}${LF}"*)
180+
# grep indeed matched the last line - it's our remote
173181
# default branch name for checkouts with no layout:
174182
upstream_type=${GIT_SVN_ID:-git-svn}
175-
else
183+
;;
184+
*)
185+
# the commit message includes one of our remotes, but
186+
# it's not at the last line. is $svn_upstream junk?
176187
upstream_type=${svn_upstream#/}
177-
fi
188+
;;
189+
esac
178190
elif [[ "svn+git" = "$upstream_type" ]]; then
179191
upstream_type="@{upstream}"
180192
fi

0 commit comments

Comments
 (0)