From: David Mandelberg Date: Thu, 06 Mar 2025 17:12:46 GMT Subject: Re: [PATCH v2 2/2] completion: fix bugs with slashes in remote names Message-ID: <7f00dc07-5905-4571-84f4-e50c39867818@mandelberg.org> In-Reply-To: <2bed28ca-b106-474d-9bac-db377bc51949@gmail.com> Op 2025-03-06 om 11:35 schreef Phillip Wood: > Hi David > > On 05/03/2025 20:50, David Mandelberg wrote: >> David Mandelberg schreef op 2025-03-04 19:09: >>> Previously, some calls to for-each-ref passed fixed numbers of path >>> components to strip from refs, assuming that remote names had no slashes >>> in them. This made completions like: >>> >>> git push github/dseomn :com >>> >>> Result in: >>> >>> git push github/dseomn :dseomn/completion-remote-slash >>> >>> With this patch, it instead results in: >>> >>> git push github/dseomn :completion-remote-slash >>> >>> Signed-off-by: David Mandelberg >>> --- >>>   contrib/completion/git-completion.bash |  38 +++++- >>>   t/t9902-completion.sh                  | 180 ++++++++++++++++++++++--- >>>   2 files changed, 189 insertions(+), 29 deletions(-) >>> >>> diff --git a/contrib/completion/git-completion.bash b/contrib/ >>> completion/git-completion.bash >>> index 5fdc71208e..450fabc901 100644 >>> --- a/contrib/completion/git-completion.bash >>> +++ b/contrib/completion/git-completion.bash >>> @@ -790,16 +790,39 @@ __git_tags () >>>   __git_dwim_remote_heads () >>>   { >>>       local pfx="${1-}" cur_="${2-}" sfx="${3-}" >>> -    local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format >>> specifiers >>>       # employ the heuristic used by git checkout and git switch >>>       # Try to find a remote branch that cur_es the completion word >>>       # but only output if the branch name is unique >>> -    __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ >>> -        --sort="refname:strip=3" \ >>> -        ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \ >>> -        "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \ >>> -    uniq -u >>> +    local awk_script=' >>> +    function casemap(s) { >>> +        if (ENVIRON["IGNORE_CASE"]) >>> +            return tolower(s) >>> +        else >>> +            return s >>> +    } >>> +    BEGIN { >>> +        split(ENVIRON["REMOTES"], remotes, /\n/) >>> +        for (i in remotes) >>> +            remotes[i] = "refs/remotes/" casemap(remotes[i]) >>> +        cur_ = casemap(ENVIRON["CUR_"]) >>> +    } >>> +    { >>> +        ref_case = casemap($0) >>> +        for (i in remotes) { >>> +            if (index(ref_case, remotes[i] "/" cur_) == 1) { >>> +                branch = substr($0, length(remotes[i] "/") + 1) >>> +                print ENVIRON["PFX"] branch ENVIRON["SFX"] >>> +                break >>> +            } >>> +        } >>> +    } >>> +    ' >>> +    __git for-each-ref --format='%(refname)' | >>> +        PFX="$pfx" SFX="$sfx" CUR_="$cur_" \ >>> +            IGNORE_CASE=${GIT_COMPLETION_IGNORE_CASE+1} \ >>> +            REMOTES="$(__git_remotes | sort -r)" awk "$awk_script" | >>> +        sort | uniq -u > > I wonder if we can use "sort -u" here and drop uniq. It isn't quite the > same as it will remove entries that are equal according to the current > locale but I don't think that should matter. That's what I thought when I first saw that, but `uniq -u` removes duplicates: $ printf 'a\nb\na\n' | sort | uniq -u b $ printf 'a\nb\na\n' | sort -u a b >> I realized that this sends refs to awk that it doesn't need to. I'll >> apply this diff to the next version of this patch: > > With that tweak this looks good. If there are no glob characters then > "git for-each-ref" does a prefix match so strictly speaking you don't > need the '**' but I don't think it matters in practice. I had one > thought below but if you don't feel like spending more that on this I > think what you have here is fine. > > The filtering is O(number of remote refs * number of remotes). If we > could sort the list of remotes and remote refs in the same order then we > can reduce this to O(number of remote refs + number of remotes) by doing > (in pseudo code) > >     for ($ref in $remote_refs) { >     while (!starts_with($ref, "refs/remotes/$remote[$i]")) >         $i++; >     if (starts_with($ref, "refs/remotes/$remote[$i]/$cur_) >         print $ref; >     } > > I think we can get "git for-each-ref --sort=-refname" and "sort -r" to > agree on a sorting order by setting LC_COLLATE=C, otherwise "sort" will > sort according the the current locale whereas git sorts by bytes. On my desktop, a similar-ish loop takes about 0.1s with 1000*1000 iterations. 1000 remote branches seems plausible but on the high side to me, and 1000 remotes seems very high to me. Do you still think it's worth optimizing? I do think your solution would work, but I think it would take a decent amount of testing to avoid the collation issues you mentioned, and off-by-one errors and the like. So I'd rather not do it unless there's a practical performance advantage, not just theoretical. (Which is what I thought about minimizing forks too, before I learned that it was a practical issue.) $ time seq 1 1000 | LONG="$(seq 1 1000)" awk 'BEGIN { split(ENVIRON["LONG"], long, /\n/); } { for (i in long) { if ($0 == long[i]) { print; } } }' > /dev/null real 0m0,092s user 0m0,067s sys 0m0,028s > Best Wishes > > Phillip > >> diff --git a/contrib/completion/git-completion.bash b/contrib/ >> completion/git-completion.bash >> index 450fabc901..c9d014070c 100644 >> --- a/contrib/completion/git-completion.bash >> +++ b/contrib/completion/git-completion.bash >> @@ -818,7 +818,7 @@ __git_dwim_remote_heads () >>                  } >>          } >>          ' >> -       __git for-each-ref --format='%(refname)' | >> +       __git for-each-ref --format='%(refname)' 'refs/remotes/**' | >>                  PFX="$pfx" SFX="$sfx" CUR_="$cur_" \ >>                          IGNORE_CASE=${GIT_COMPLETION_IGNORE_CASE+1} \ >>                          REMOTES="$(__git_remotes | sort -r)" awk >> "$awk_script" | >> >>>   } >>>   # Lists refs from the local (by default) or from a remote repository. >>> @@ -905,7 +928,8 @@ __git_refs () >>>               case "HEAD" in >>>               $match*|$umatch*)    echo "${pfx}HEAD$sfx" ;; >>>               esac >>> -            __git for-each-ref -- >>> format="$fer_pfx%(refname:strip=3)$sfx" \ >>> +            local strip="$(__git_count_path_components "refs/ >>> remotes/$remote")" >>> +            __git for-each-ref -- >>> format="$fer_pfx%(refname:strip=$strip)$sfx" \ >>>                   ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \ >>>                   "refs/remotes/$remote/$match*" \ >>>                   "refs/remotes/$remote/$match*/**" >>> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh >>> index 015289c776..343b8cd191 100755 >>> --- a/t/t9902-completion.sh >>> +++ b/t/t9902-completion.sh >>> @@ -149,7 +149,8 @@ fi >>>   test_expect_success 'setup for __git_find_repo_path/__gitdir tests' ' >>>       mkdir -p subdir/subsubdir && >>>       mkdir -p non-repo && >>> -    git init -b main otherrepo >>> +    git init -b main otherrepo && >>> +    git init -b main slashrepo >>>   ' >>>   test_expect_success '__git_find_repo_path - from command line >>> (through $__git_dir)' ' >>> @@ -674,6 +675,13 @@ test_expect_success 'setup for ref completion' ' >>>       ) && >>>       git remote add other "$ROOT/otherrepo/.git" && >>>       git fetch --no-tags other && >>> +    ( >>> +        cd slashrepo && >>> +        git commit --allow-empty -m initial && >>> +        git branch -m main branch/with/slash >>> +    ) && >>> +    git remote add remote/with/slash "$ROOT/slashrepo/.git" && >>> +    git fetch --no-tags remote/with/slash && >>>       rm -f .git/FETCH_HEAD && >>>       git init thirdrepo >>>   ' >>> @@ -686,6 +694,8 @@ test_expect_success '__git_refs - simple' ' >>>       other/HEAD >>>       other/branch-in-other >>>       other/main-in-other >>> +    remote/with/slash/HEAD >>> +    remote/with/slash/branch/with/slash >>>       matching-tag >>>       EOF >>>       ( >>> @@ -702,6 +712,8 @@ test_expect_success '__git_refs - full refs' ' >>>       refs/remotes/other/HEAD >>>       refs/remotes/other/branch-in-other >>>       refs/remotes/other/main-in-other >>> +    refs/remotes/remote/with/slash/HEAD >>> +    refs/remotes/remote/with/slash/branch/with/slash >>>       refs/tags/matching-tag >>>       EOF >>>       ( >>> @@ -767,6 +779,19 @@ test_expect_success '__git_refs - configured >>> remote' ' >>>       test_cmp expected "$actual" >>>   ' >>> +test_expect_success '__git_refs - configured remote - with slash' ' >>> +    cat >expected <<-EOF && >>> +    HEAD >>> +    HEAD >>> +    branch/with/slash >>> +    EOF >>> +    ( >>> +        cur= && >>> +        __git_refs remote/with/slash >"$actual" >>> +    ) && >>> +    test_cmp expected "$actual" >>> +' >>> + >>>   test_expect_success '__git_refs - configured remote - full refs' ' >>>       cat >expected <<-EOF && >>>       HEAD >>> @@ -909,17 +934,19 @@ test_expect_success '__git_refs - unique remote >>> branches for git checkout DWIMer >>>       other/ambiguous >>>       other/branch-in-other >>>       other/main-in-other >>> -    remote/ambiguous >>> -    remote/branch-in-remote >>> +    remote/with/slash/HEAD >>> +    remote/with/slash/ambiguous >>> +    remote/with/slash/branch-in-remote >>> +    remote/with/slash/branch/with/slash >>>       matching-tag >>> -    HEAD >>>       branch-in-other >>>       branch-in-remote >>> +    branch/with/slash >>>       main-in-other >>>       EOF >>>       for remote_ref in refs/remotes/other/ambiguous \ >>> -        refs/remotes/remote/ambiguous \ >>> -        refs/remotes/remote/branch-in-remote >>> +        refs/remotes/remote/with/slash/ambiguous \ >>> +        refs/remotes/remote/with/slash/branch-in-remote >>>       do >>>           git update-ref $remote_ref main && >>>           test_when_finished "git update-ref -d $remote_ref" || return 1 >>> @@ -939,6 +966,8 @@ test_expect_success '__git_refs - after --opt=' ' >>>       other/HEAD >>>       other/branch-in-other >>>       other/main-in-other >>> +    remote/with/slash/HEAD >>> +    remote/with/slash/branch/with/slash >>>       matching-tag >>>       EOF >>>       ( >>> @@ -955,6 +984,8 @@ test_expect_success '__git_refs - after --opt= - >>> full refs' ' >>>       refs/remotes/other/HEAD >>>       refs/remotes/other/branch-in-other >>>       refs/remotes/other/main-in-other >>> +    refs/remotes/remote/with/slash/HEAD >>> +    refs/remotes/remote/with/slash/branch/with/slash >>>       refs/tags/matching-tag >>>       EOF >>>       ( >>> @@ -972,6 +1003,8 @@ test_expect_success '__git refs - excluding refs' ' >>>       ^other/HEAD >>>       ^other/branch-in-other >>>       ^other/main-in-other >>> +    ^remote/with/slash/HEAD >>> +    ^remote/with/slash/branch/with/slash >>>       ^matching-tag >>>       EOF >>>       ( >>> @@ -988,6 +1021,8 @@ test_expect_success '__git refs - excluding full >>> refs' ' >>>       ^refs/remotes/other/HEAD >>>       ^refs/remotes/other/branch-in-other >>>       ^refs/remotes/other/main-in-other >>> +    ^refs/remotes/remote/with/slash/HEAD >>> +    ^refs/remotes/remote/with/slash/branch/with/slash >>>       ^refs/tags/matching-tag >>>       EOF >>>       ( >>> @@ -1015,6 +1050,8 @@ test_expect_success '__git_refs - do not filter >>> refs unless told so' ' >>>       other/branch-in-other >>>       other/main-in-other >>>       other/matching/branch-in-other >>> +    remote/with/slash/HEAD >>> +    remote/with/slash/branch/with/slash >>>       matching-tag >>>       matching/tag >>>       EOF >>> @@ -1135,6 +1172,8 @@ test_expect_success '__git_complete_refs - >>> simple' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       matching-tag Z >>>       EOF >>>       ( >>> @@ -1173,6 +1212,20 @@ test_expect_success '__git_complete_refs - >>> remote' ' >>>       test_cmp expected out >>>   ' >>> +test_expect_success '__git_complete_refs - remote - with slash' ' >>> +    sed -e "s/Z$//" >expected <<-EOF && >>> +    HEAD Z >>> +    HEAD Z >>> +    branch/with/slash Z >>> +    EOF >>> +    ( >>> +        cur= && >>> +        __git_complete_refs --remote=remote/with/slash && >>> +        print_comp >>> +    ) && >>> +    test_cmp expected out >>> +' >>> + >>>   test_expect_success '__git_complete_refs - track' ' >>>       sed -e "s/Z$//" >expected <<-EOF && >>>       HEAD Z >>> @@ -1181,9 +1234,11 @@ test_expect_success '__git_complete_refs - >>> track' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       matching-tag Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main-in-other Z >>>       EOF >>>       ( >>> @@ -1228,6 +1283,8 @@ test_expect_success '__git_complete_refs - >>> suffix' ' >>>       other/HEAD. >>>       other/branch-in-other. >>>       other/main-in-other. >>> +    remote/with/slash/HEAD. >>> +    remote/with/slash/branch/with/slash. >>>       matching-tag. >>>       EOF >>>       ( >>> @@ -1253,6 +1310,20 @@ test_expect_success >>> '__git_complete_fetch_refspecs - simple' ' >>>       test_cmp expected out >>>   ' >>> +test_expect_success '__git_complete_fetch_refspecs - with slash' ' >>> +    sed -e "s/Z$//" >expected <<-EOF && >>> +    HEAD:HEAD Z >>> +    HEAD:HEAD Z >>> +    branch/with/slash:branch/with/slash Z >>> +    EOF >>> +    ( >>> +        cur= && >>> +        __git_complete_fetch_refspecs remote/with/slash && >>> +        print_comp >>> +    ) && >>> +    test_cmp expected out >>> +' >>> + >>>   test_expect_success '__git_complete_fetch_refspecs - matching' ' >>>       sed -e "s/Z$//" >expected <<-EOF && >>>       branch-in-other:branch-in-other Z >>> @@ -1333,8 +1404,8 @@ test_expect_success >>> '__git_complete_worktree_paths with -C' ' >>>   test_expect_success 'git switch - with no options, complete local >>> branches and unique remote branch names for DWIM logic' ' >>>       test_completion "git switch " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1480,8 +1551,8 @@ test_expect_success 'git-bisect - existing view >>> subcommand is recognized and ena >>>   test_expect_success 'git checkout - completes refs and unique >>> remote branches for DWIM' ' >>>       test_completion "git checkout " <<-\EOF >>>       HEAD Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1489,6 +1560,8 @@ test_expect_success 'git checkout - completes >>> refs and unique remote branches fo >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1508,8 +1581,8 @@ test_expect_success 'git switch - with >>> GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl >>>   test_expect_success 'git switch - --guess overrides >>> GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and >>> unique remote names for DWIM logic' ' >>>       GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch >>> --guess " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1518,8 +1591,8 @@ test_expect_success 'git switch - --guess >>> overrides GIT_COMPLETION_CHECKOUT_NO_G >>>   test_expect_success 'git switch - a later --guess overrides >>> previous --no-guess, complete local and remote unique branches for >>> DWIM' ' >>>       test_completion "git switch --no-guess --guess " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1542,14 +1615,16 @@ test_expect_success 'git checkout - with >>> GIT_COMPLETION_NO_GUESS=1 only complete >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>>   test_expect_success 'git checkout - --guess overrides >>> GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches >>> for DWIM' ' >>>       GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git >>> checkout --guess " <<-\EOF >>>       HEAD Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1557,6 +1632,8 @@ test_expect_success 'git checkout - --guess >>> overrides GIT_COMPLETION_NO_GUESS=1, >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1569,14 +1646,16 @@ test_expect_success 'git checkout - with -- >>> no-guess, only completes refs' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>>   test_expect_success 'git checkout - a later --guess overrides >>> previous --no-guess, complete refs and unique remote branches for >>> DWIM' ' >>>       test_completion "git checkout --no-guess --guess " <<-\EOF >>>       HEAD Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1584,6 +1663,8 @@ test_expect_success 'git checkout - a later -- >>> guess overrides previous --no-gues >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1596,6 +1677,8 @@ test_expect_success 'git checkout - a later -- >>> no-guess overrides previous --gues >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1609,6 +1692,8 @@ test_expect_success 'git checkout - with >>> checkout.guess = false, only completes >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1616,8 +1701,8 @@ test_expect_success 'git checkout - with >>> checkout.guess = true, completes refs a >>>       test_config checkout.guess true && >>>       test_completion "git checkout " <<-\EOF >>>       HEAD Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1625,6 +1710,8 @@ test_expect_success 'git checkout - with >>> checkout.guess = true, completes refs a >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1632,8 +1719,8 @@ test_expect_success 'git checkout - a later -- >>> guess overrides previous checkout. >>>       test_config checkout.guess false && >>>       test_completion "git checkout --guess " <<-\EOF >>>       HEAD Z >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -1641,6 +1728,8 @@ test_expect_success 'git checkout - a later -- >>> guess overrides previous checkout. >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1654,6 +1743,8 @@ test_expect_success 'git checkout - a later -- >>> no-guess overrides previous checko >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1666,6 +1757,8 @@ test_expect_success 'git switch - with -- >>> detach, complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1678,6 +1771,8 @@ test_expect_success 'git checkout - with -- >>> detach, complete only references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1850,6 +1945,8 @@ test_expect_success 'git switch - with -d, >>> complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1862,6 +1959,8 @@ test_expect_success 'git checkout - with -d, >>> complete only references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1870,11 +1969,15 @@ test_expect_success 'git switch - with -- >>> track, complete only remote branches' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>       test_completion "git switch -t " <<-\EOF >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1883,11 +1986,15 @@ test_expect_success 'git checkout - with -- >>> track, complete only remote branches' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>       test_completion "git checkout -t " <<-\EOF >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1907,6 +2014,8 @@ test_expect_success 'git checkout - with --no- >>> track, complete only local referen >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1919,6 +2028,8 @@ test_expect_success 'git switch - with -c, >>> complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1931,6 +2042,8 @@ test_expect_success 'git switch - with -C, >>> complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1943,6 +2056,8 @@ test_expect_success 'git switch - with -c and >>> --track, complete all references' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1955,6 +2070,8 @@ test_expect_success 'git switch - with -C and >>> --track, complete all references' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1967,6 +2084,8 @@ test_expect_success 'git switch - with -c and >>> --no-track, complete all reference >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1979,6 +2098,8 @@ test_expect_success 'git switch - with -C and >>> --no-track, complete all reference >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -1991,6 +2112,8 @@ test_expect_success 'git checkout - with -b, >>> complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2003,6 +2126,8 @@ test_expect_success 'git checkout - with -B, >>> complete all references' ' >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2015,6 +2140,8 @@ test_expect_success 'git checkout - with -b and >>> --track, complete all references >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2027,6 +2154,8 @@ test_expect_success 'git checkout - with -B and >>> --track, complete all references >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2039,6 +2168,8 @@ test_expect_success 'git checkout - with -b and >>> --no-track, complete all referen >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2051,13 +2182,15 @@ test_expect_success 'git checkout - with -B >>> and --no-track, complete all referen >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>>   test_expect_success 'git switch - for -c, complete local branches >>> and unique remote branches' ' >>>       test_completion "git switch -c " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2066,8 +2199,8 @@ test_expect_success 'git switch - for -c, >>> complete local branches and unique rem >>>   test_expect_success 'git switch - for -C, complete local branches >>> and unique remote branches' ' >>>       test_completion "git switch -C " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2104,8 +2237,8 @@ test_expect_success 'git switch - for -C with >>> --no-track, complete local branche >>>   test_expect_success 'git checkout - for -b, complete local branches >>> and unique remote branches' ' >>>       test_completion "git checkout -b " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2114,8 +2247,8 @@ test_expect_success 'git checkout - for -b, >>> complete local branches and unique r >>>   test_expect_success 'git checkout - for -B, complete local branches >>> and unique remote branches' ' >>>       test_completion "git checkout -B " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2152,8 +2285,8 @@ test_expect_success 'git checkout - for -B with >>> --no-track, complete local branc >>>   test_expect_success 'git switch - with --orphan completes local >>> branch names and unique remote branch names' ' >>>       test_completion "git switch --orphan " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2168,8 +2301,8 @@ test_expect_success 'git switch - --orphan with >>> branch already provided complete >>>   test_expect_success 'git checkout - with --orphan completes local >>> branch names and unique remote branch names' ' >>>       test_completion "git checkout --orphan " <<-\EOF >>> -    HEAD Z >>>       branch-in-other Z >>> +    branch/with/slash Z >>>       main Z >>>       main-in-other Z >>>       matching-branch Z >>> @@ -2185,6 +2318,8 @@ test_expect_success 'git checkout - --orphan >>> with branch already provided comple >>>       other/HEAD Z >>>       other/branch-in-other Z >>>       other/main-in-other Z >>> +    remote/with/slash/HEAD Z >>> +    remote/with/slash/branch/with/slash Z >>>       EOF >>>   ' >>> @@ -2199,7 +2334,8 @@ test_expect_success 'git restore completes >>> modified files' ' >>>   test_expect_success 'teardown after ref completion' ' >>>       git branch -d matching-branch && >>>       git tag -d matching-tag && >>> -    git remote remove other >>> +    git remote remove other && >>> +    git remote remove remote/with/slash >>>   ' >> >