Skip to content

Commit 1cd23e9

Browse files
szedergitster
authored andcommitted
completion: don't use __gitdir() for git commands
Several completion functions contain the following pattern to run git commands respecting the path to the repository specified on the command line: git --git-dir="$(__gitdir)" <cmd> <options> This imposes the overhead of fork()ing a subshell for the command substitution and potentially fork()+exec()ing 'git rev-parse' inside __gitdir(). Now, if neither '--gitdir=<path>' nor '-C <path>' options are specified on the command line, then those git commands are perfectly capable to discover the repository on their own. If either one or both of those options are specified on the command line, then, again, the git commands could discover the repository, if we pass them all of those options from the command line. This means we don't have to run __gitdir() at all for git commands and can spare its fork()+exec() overhead. Use Bash parameter expansions to check the $__git_dir variable and $__git_C_args array and to assemble the appropriate '--git-dir=<path>' and '-C <path>' options if either one or both are present on the command line. These parameter expansions are, however, rather long, so instead of changing all git executions and make already long lines even longer, encapsulate running git with '--git-dir=<path> -C <path>' options into the new __git() wrapper function. Furthermore, this wrapper function will also enable us to silence error messages from git commands uniformly in one place in a later commit. There's one tricky case, though: in __git_refs() local refs are listed with 'git for-each-ref', where "local" is not necessarily the repository we are currently in, but it might mean a remote repository in the filesystem (e.g. listing refs for 'git fetch /some/other/repo <TAB>'). Use one-shot variable assignment to override $__git_dir with the path of the repository where the refs should come from. Although one-shot variable assignments in front of shell functions are to be avoided in our scripts in general, in the Bash completion script we can do that safely. Signed-off-by: SZEDER Gábor <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 80ac074 commit 1cd23e9

File tree

1 file changed

+31
-29
lines changed

1 file changed

+31
-29
lines changed

contrib/completion/git-completion.bash

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ __gitdir ()
6161
fi
6262
}
6363

64+
# Runs git with all the options given as argument, respecting any
65+
# '--git-dir=<path>' and '-C <path>' options present on the command line
66+
__git ()
67+
{
68+
git ${__git_C_args:+"${__git_C_args[@]}"} \
69+
${__git_dir:+--git-dir="$__git_dir"} "$@"
70+
}
71+
6472
# The following function is based on code from:
6573
#
6674
# bash_completion - programmable completion functions for bash 3.2+
@@ -287,13 +295,11 @@ __gitcomp_file ()
287295
# argument, and using the options specified in the second argument.
288296
__git_ls_files_helper ()
289297
{
290-
local dir="$(__gitdir)"
291-
292298
if [ "$2" == "--committable" ]; then
293-
git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" diff-index --name-only --relative HEAD
299+
__git -C "$1" diff-index --name-only --relative HEAD
294300
else
295301
# NOTE: $2 is not quoted in order to support multiple options
296-
git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
302+
__git -C "$1" ls-files --exclude-standard $2
297303
fi 2>/dev/null
298304
}
299305

@@ -323,8 +329,7 @@ __git_heads ()
323329
{
324330
local dir="$(__gitdir)"
325331
if [ -d "$dir" ]; then
326-
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
327-
refs/heads
332+
__git for-each-ref --format='%(refname:short)' refs/heads
328333
return
329334
fi
330335
}
@@ -333,8 +338,7 @@ __git_tags ()
333338
{
334339
local dir="$(__gitdir)"
335340
if [ -d "$dir" ]; then
336-
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
337-
refs/tags
341+
__git for-each-ref --format='%(refname:short)' refs/tags
338342
return
339343
fi
340344
}
@@ -385,14 +389,14 @@ __git_refs ()
385389
refs="refs/tags refs/heads refs/remotes"
386390
;;
387391
esac
388-
git --git-dir="$dir" for-each-ref --format="$pfx%($format)" \
392+
__git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \
389393
$refs
390394
if [ -n "$track" ]; then
391395
# employ the heuristic used by git checkout
392396
# Try to find a remote branch that matches the completion word
393397
# but only output if the branch name is unique
394398
local ref entry
395-
git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
399+
__git for-each-ref --shell --format="ref=%(refname:short)" \
396400
"refs/remotes/" | \
397401
while read -r entry; do
398402
eval "$entry"
@@ -406,7 +410,7 @@ __git_refs ()
406410
fi
407411
case "$cur" in
408412
refs|refs/*)
409-
git --git-dir="$dir" ls-remote "$remote" "$cur*" 2>/dev/null | \
413+
__git ls-remote "$remote" "$cur*" 2>/dev/null | \
410414
while read -r hash i; do
411415
case "$i" in
412416
*^{}) ;;
@@ -417,10 +421,10 @@ __git_refs ()
417421
*)
418422
if [ "$list_refs_from" = remote ]; then
419423
echo "HEAD"
420-
git --git-dir="$dir" for-each-ref --format="%(refname:short)" \
424+
__git for-each-ref --format="%(refname:short)" \
421425
"refs/remotes/$remote/" 2>/dev/null | sed -e "s#^$remote/##"
422426
else
423-
git --git-dir="$dir" ls-remote "$remote" HEAD \
427+
__git ls-remote "$remote" HEAD \
424428
"refs/tags/*" "refs/heads/*" "refs/remotes/*" 2>/dev/null |
425429
while read -r hash i; do
426430
case "$i" in
@@ -447,7 +451,7 @@ __git_refs2 ()
447451
__git_refs_remotes ()
448452
{
449453
local i hash
450-
git --git-dir="$(__gitdir)" ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
454+
__git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
451455
while read -r hash i; do
452456
echo "$i:refs/remotes/$1/${i#refs/heads/}"
453457
done
@@ -457,7 +461,7 @@ __git_remotes ()
457461
{
458462
local d="$(__gitdir)"
459463
test -d "$d/remotes" && ls -1 "$d/remotes"
460-
git --git-dir="$d" remote
464+
__git remote
461465
}
462466

463467
# Returns true if $1 matches the name of a configured remote, false otherwise.
@@ -523,7 +527,7 @@ __git_complete_revlist_file ()
523527
*) pfx="$ref:$pfx" ;;
524528
esac
525529

526-
__gitcomp_nl "$(git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
530+
__gitcomp_nl "$(__git ls-tree "$ls" 2>/dev/null \
527531
| sed '/^100... blob /{
528532
s,^.* ,,
529533
s,$, ,
@@ -801,7 +805,7 @@ __git_compute_porcelain_commands ()
801805
__git_get_config_variables ()
802806
{
803807
local section="$1" i IFS=$'\n'
804-
for i in $(git --git-dir="$(__gitdir)" config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
808+
for i in $(__git config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
805809
echo "${i#$section.}"
806810
done
807811
}
@@ -819,8 +823,7 @@ __git_aliases ()
819823
# __git_aliased_command requires 1 argument
820824
__git_aliased_command ()
821825
{
822-
local word cmdline=$(git --git-dir="$(__gitdir)" \
823-
config --get "alias.$1" 2>/dev/null)
826+
local word cmdline=$(__git config --get "alias.$1" 2>/dev/null)
824827
for word in $cmdline; do
825828
case "$word" in
826829
\!gitk|gitk)
@@ -896,7 +899,7 @@ __git_get_option_value ()
896899
done
897900

898901
if [ -n "$config_key" ] && [ -z "$result" ]; then
899-
result="$(git --git-dir="$(__gitdir)" config "$config_key")"
902+
result="$(__git config "$config_key")"
900903
fi
901904

902905
echo "$result"
@@ -1237,7 +1240,7 @@ _git_commit ()
12371240
return
12381241
esac
12391242

1240-
if git --git-dir="$(__gitdir)" rev-parse --verify --quiet HEAD >/dev/null; then
1243+
if __git rev-parse --verify --quiet HEAD >/dev/null; then
12411244
__git_complete_index_file "--committable"
12421245
else
12431246
# This is the first commit
@@ -1839,7 +1842,7 @@ _git_send_email ()
18391842
case "$prev" in
18401843
--to|--cc|--bcc|--from)
18411844
__gitcomp "
1842-
$(git --git-dir="$(__gitdir)" send-email --dump-aliases 2>/dev/null)
1845+
$(__git send-email --dump-aliases 2>/dev/null)
18431846
"
18441847
return
18451848
;;
@@ -1871,7 +1874,7 @@ _git_send_email ()
18711874
;;
18721875
--to=*|--cc=*|--bcc=*|--from=*)
18731876
__gitcomp "
1874-
$(git --git-dir="$(__gitdir)" send-email --dump-aliases 2>/dev/null)
1877+
$(__git send-email --dump-aliases 2>/dev/null)
18751878
" "" "${cur#--*=}"
18761879
return
18771880
;;
@@ -1966,7 +1969,7 @@ __git_config_get_set_variables ()
19661969
c=$((--c))
19671970
done
19681971

1969-
git --git-dir="$(__gitdir)" config $config_file --name-only --list 2>/dev/null
1972+
__git config $config_file --name-only --list 2>/dev/null
19701973
}
19711974

19721975
_git_config ()
@@ -2001,9 +2004,8 @@ _git_config ()
20012004
remote.*.push)
20022005
local remote="${prev#remote.}"
20032006
remote="${remote%.push}"
2004-
__gitcomp_nl "$(git --git-dir="$(__gitdir)" \
2005-
for-each-ref --format='%(refname):%(refname)' \
2006-
refs/heads)"
2007+
__gitcomp_nl "$(__git for-each-ref
2008+
--format='%(refname):%(refname)' refs/heads)"
20072009
return
20082010
;;
20092011
pull.twohead|pull.octopus)
@@ -2591,12 +2593,12 @@ _git_stash ()
25912593
if [ $cword -eq 3 ]; then
25922594
__gitcomp_nl "$(__git_refs)";
25932595
else
2594-
__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
2596+
__gitcomp_nl "$(__git stash list \
25952597
| sed -n -e 's/:.*//p')"
25962598
fi
25972599
;;
25982600
show,*|apply,*|drop,*|pop,*)
2599-
__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
2601+
__gitcomp_nl "$(__git stash list \
26002602
| sed -n -e 's/:.*//p')"
26012603
;;
26022604
*)

0 commit comments

Comments
 (0)