]> git.ipfire.org Git - thirdparty/git.git/commitdiff
completion: respect 'git -C <path>'
authorSZEDER Gábor <szeder.dev@gmail.com>
Fri, 3 Feb 2017 02:48:24 +0000 (03:48 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sat, 4 Feb 2017 06:18:41 +0000 (22:18 -0800)
'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 <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
contrib/completion/git-completion.bash
t/t9902-completion.sh

index 262760f8c00ae27e26f72e9ada98d994115a4947..f2d939d358dff9820912e774b9f5c2eb17bd684b 100644 (file)
@@ -39,7 +39,11 @@ esac
 __gitdir ()
 {
        if [ -z "${1-}" ]; then
-               if [ -n "${__git_dir-}" ]; then
+               if [ -n "${__git_C_args-}" ]; then
+                       git "${__git_C_args[@]}" \
+                               ${__git_dir:+--git-dir="$__git_dir"} \
+                               rev-parse --absolute-git-dir 2>/dev/null
+               elif [ -n "${__git_dir-}" ]; then
                        test -d "$__git_dir" || return 1
                        echo "$__git_dir"
                elif [ -n "${GIT_DIR-}" ]; then
@@ -286,10 +290,10 @@ __git_ls_files_helper ()
        local dir="$(__gitdir)"
 
        if [ "$2" == "--committable" ]; then
-               git --git-dir="$dir" -C "$1" diff-index --name-only --relative HEAD
+               git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" diff-index --name-only --relative HEAD
        else
                # NOTE: $2 is not quoted in order to support multiple options
-               git --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
+               git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
        fi 2>/dev/null
 }
 
@@ -519,7 +523,7 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
+               __gitcomp_nl "$(git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
                                | sed '/^100... blob /{
                                           s,^.*        ,,
                                           s,$, ,
@@ -2792,6 +2796,7 @@ _git_worktree ()
 __git_main ()
 {
        local i c=1 command __git_dir
+       local __git_C_args C_args_count=0
 
        while [ $c -lt $cword ]; do
                i="${words[c]}"
@@ -2800,7 +2805,11 @@ __git_main ()
                --git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
                --bare)      __git_dir="." ;;
                --help) command="help"; break ;;
-               -c|-C|--work-tree|--namespace) ((c++)) ;;
+               -c|--work-tree|--namespace) ((c++)) ;;
+               -C)     __git_C_args[C_args_count++]=-C
+                       ((c++))
+                       __git_C_args[C_args_count++]="${words[c]}"
+                       ;;
                -*) ;;
                *) command="$i"; break ;;
                esac
index be2ed8704395d792fcc8a745980d11a98be03662..984df05b236632a549fd6b7af7762dab4febac8b 100755 (executable)
@@ -211,6 +211,87 @@ test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
        test_cmp expected "$actual"
 '
 
+test_expect_success '__gitdir - from command line while "git -C"' '
+       echo "$ROOT/.git" >expected &&
+       (
+               __git_dir="$ROOT/.git" &&
+               __git_C_args=(-C otherrepo) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - relative dir from command line and "git -C"' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd subdir &&
+               __git_dir="otherrepo/.git" &&
+               __git_C_args=(-C ..) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while "git -C"' '
+       echo "$ROOT/.git" >expected &&
+       (
+               GIT_DIR="$ROOT/.git" &&
+               export GIT_DIR &&
+               __git_C_args=(-C otherrepo) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - relative dir in $GIT_DIR and "git -C"' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd subdir &&
+               GIT_DIR="otherrepo/.git" &&
+               export GIT_DIR &&
+               __git_C_args=(-C ..) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - "git -C" while .git directory in cwd' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               __git_C_args=(-C otherrepo) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - "git -C" while cwd is a .git directory' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd .git &&
+               __git_C_args=(-C .. -C otherrepo) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - "git -C" while .git directory in parent' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd subdir &&
+               __git_C_args=(-C .. -C otherrepo) &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing path in "git -C"' '
+       (
+               __git_C_args=(-C non-existing) &&
+               test_must_fail __gitdir >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
 test_expect_success '__gitdir - non-existing path in $__git_dir' '
        (
                __git_dir="non-existing" &&
@@ -785,6 +866,12 @@ test_expect_success 'checkout completes ref names' '
        EOF
 '
 
+test_expect_success 'git -C <path> checkout uses the right repo' '
+       test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
+       branch-in-other Z
+       EOF
+'
+
 test_expect_success 'show completes all refs' '
        test_completion "git show m" <<-\EOF
        master Z