Skip to content

Commit 80ac074

Browse files
szedergitster
authored andcommitted
completion: respect 'git -C <path>'
'git -C <path>' option(s) on the command line should be taken into account during completion, because - like '--git-dir=<path>', it can lead us to a different repository, - a few git commands executed in the completion script do care about in which directory they are executed, and - the command for which we are providing completion might care about in which directory it will be executed. However, unlike '--git-dir=<path>', the '-C <path>' option can be specified multiple times and their effect is cumulative, so we can't just store a single '<path>' in a variable. Nor can we simply concatenate a path from '-C <path1> -C <path2> ...', because e.g. (in an arguably pathological corner case) a relative path might be followed by an absolute path. Instead, store all '-C <path>' options word by word in the $__git_C_args array in the main git completion function, and pass this array, if present, to 'git rev-parse --absolute-git-dir' when discovering the repository in __gitdir(), and let it take care of multiple options, relative paths, absolute paths and everything. Also pass all '-C <path> options via the $__git_C_args array to those git executions which require a worktree and for which it matters from which directory they are executed from. There are only three such cases: - 'git diff-index' and 'git ls-files' in __git_ls_files_helper() used for git-aware filename completion, and - the 'git ls-tree' used for completing the 'ref:path' notation. The other git commands executed in the completion script don't need these '-C <path>' options, because __gitdir() already took those options into account. It would not hurt them, either, but let's not induce unnecessary code churn. Signed-off-by: SZEDER Gábor <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a2f5a87 commit 80ac074

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

contrib/completion/git-completion.bash

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ esac
3939
__gitdir ()
4040
{
4141
if [ -z "${1-}" ]; then
42-
if [ -n "${__git_dir-}" ]; then
42+
if [ -n "${__git_C_args-}" ]; then
43+
git "${__git_C_args[@]}" \
44+
${__git_dir:+--git-dir="$__git_dir"} \
45+
rev-parse --absolute-git-dir 2>/dev/null
46+
elif [ -n "${__git_dir-}" ]; then
4347
test -d "$__git_dir" || return 1
4448
echo "$__git_dir"
4549
elif [ -n "${GIT_DIR-}" ]; then
@@ -286,10 +290,10 @@ __git_ls_files_helper ()
286290
local dir="$(__gitdir)"
287291

288292
if [ "$2" == "--committable" ]; then
289-
git --git-dir="$dir" -C "$1" diff-index --name-only --relative HEAD
293+
git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" diff-index --name-only --relative HEAD
290294
else
291295
# NOTE: $2 is not quoted in order to support multiple options
292-
git --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
296+
git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
293297
fi 2>/dev/null
294298
}
295299

@@ -519,7 +523,7 @@ __git_complete_revlist_file ()
519523
*) pfx="$ref:$pfx" ;;
520524
esac
521525

522-
__gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
526+
__gitcomp_nl "$(git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
523527
| sed '/^100... blob /{
524528
s,^.* ,,
525529
s,$, ,
@@ -2792,6 +2796,7 @@ _git_worktree ()
27922796
__git_main ()
27932797
{
27942798
local i c=1 command __git_dir
2799+
local __git_C_args C_args_count=0
27952800

27962801
while [ $c -lt $cword ]; do
27972802
i="${words[c]}"
@@ -2800,7 +2805,11 @@ __git_main ()
28002805
--git-dir) ((c++)) ; __git_dir="${words[c]}" ;;
28012806
--bare) __git_dir="." ;;
28022807
--help) command="help"; break ;;
2803-
-c|-C|--work-tree|--namespace) ((c++)) ;;
2808+
-c|--work-tree|--namespace) ((c++)) ;;
2809+
-C) __git_C_args[C_args_count++]=-C
2810+
((c++))
2811+
__git_C_args[C_args_count++]="${words[c]}"
2812+
;;
28042813
-*) ;;
28052814
*) command="$i"; break ;;
28062815
esac

t/t9902-completion.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,87 @@ test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
211211
test_cmp expected "$actual"
212212
'
213213

214+
test_expect_success '__gitdir - from command line while "git -C"' '
215+
echo "$ROOT/.git" >expected &&
216+
(
217+
__git_dir="$ROOT/.git" &&
218+
__git_C_args=(-C otherrepo) &&
219+
__gitdir >"$actual"
220+
) &&
221+
test_cmp expected "$actual"
222+
'
223+
224+
test_expect_success '__gitdir - relative dir from command line and "git -C"' '
225+
echo "$ROOT/otherrepo/.git" >expected &&
226+
(
227+
cd subdir &&
228+
__git_dir="otherrepo/.git" &&
229+
__git_C_args=(-C ..) &&
230+
__gitdir >"$actual"
231+
) &&
232+
test_cmp expected "$actual"
233+
'
234+
235+
test_expect_success '__gitdir - $GIT_DIR set while "git -C"' '
236+
echo "$ROOT/.git" >expected &&
237+
(
238+
GIT_DIR="$ROOT/.git" &&
239+
export GIT_DIR &&
240+
__git_C_args=(-C otherrepo) &&
241+
__gitdir >"$actual"
242+
) &&
243+
test_cmp expected "$actual"
244+
'
245+
246+
test_expect_success '__gitdir - relative dir in $GIT_DIR and "git -C"' '
247+
echo "$ROOT/otherrepo/.git" >expected &&
248+
(
249+
cd subdir &&
250+
GIT_DIR="otherrepo/.git" &&
251+
export GIT_DIR &&
252+
__git_C_args=(-C ..) &&
253+
__gitdir >"$actual"
254+
) &&
255+
test_cmp expected "$actual"
256+
'
257+
258+
test_expect_success '__gitdir - "git -C" while .git directory in cwd' '
259+
echo "$ROOT/otherrepo/.git" >expected &&
260+
(
261+
__git_C_args=(-C otherrepo) &&
262+
__gitdir >"$actual"
263+
) &&
264+
test_cmp expected "$actual"
265+
'
266+
267+
test_expect_success '__gitdir - "git -C" while cwd is a .git directory' '
268+
echo "$ROOT/otherrepo/.git" >expected &&
269+
(
270+
cd .git &&
271+
__git_C_args=(-C .. -C otherrepo) &&
272+
__gitdir >"$actual"
273+
) &&
274+
test_cmp expected "$actual"
275+
'
276+
277+
test_expect_success '__gitdir - "git -C" while .git directory in parent' '
278+
echo "$ROOT/otherrepo/.git" >expected &&
279+
(
280+
cd subdir &&
281+
__git_C_args=(-C .. -C otherrepo) &&
282+
__gitdir >"$actual"
283+
) &&
284+
test_cmp expected "$actual"
285+
'
286+
287+
test_expect_success '__gitdir - non-existing path in "git -C"' '
288+
(
289+
__git_C_args=(-C non-existing) &&
290+
test_must_fail __gitdir >"$actual"
291+
) &&
292+
test_must_be_empty "$actual"
293+
'
294+
214295
test_expect_success '__gitdir - non-existing path in $__git_dir' '
215296
(
216297
__git_dir="non-existing" &&
@@ -785,6 +866,12 @@ test_expect_success 'checkout completes ref names' '
785866
EOF
786867
'
787868

869+
test_expect_success 'git -C <path> checkout uses the right repo' '
870+
test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
871+
branch-in-other Z
872+
EOF
873+
'
874+
788875
test_expect_success 'show completes all refs' '
789876
test_completion "git show m" <<-\EOF
790877
master Z

0 commit comments

Comments
 (0)