Merge branch 'jc/complete-checkout'

'git -C <dir> checkout fi<TAB>' did not complete, which has been
corrected.

* jc/complete-checkout:
  completion: 'git checkout' completes untracked paths as a last resort
  completion: complete tracked paths for "git checkout"
  completion: no-op refactoring of checkout completion
main
Junio C Hamano 2026-08-25 10:53:34 -07:00
commit fee541a0d8
2 changed files with 105 additions and 39 deletions

View File

@ -1735,49 +1735,59 @@ __git_checkout_default_dwim_mode ()

_git_checkout ()
{
__git_has_doubledash && return
if ! __git_has_doubledash; then
local dwim_opt="$(__git_checkout_default_dwim_mode)"

local dwim_opt="$(__git_checkout_default_dwim_mode)"
case "$prev" in
-b|-B|--orphan)
# Complete local branches (and DWIM branch
# remote branch names) for an option argument
# specifying a new branch name. This is for
# convenience, assuming new branches are
# possibly based on pre-existing branch names.
__git_complete_refs $dwim_opt --mode="heads"
return
;;
*)
;;
esac

case "$prev" in
-b|-B|--orphan)
# Complete local branches (and DWIM branch
# remote branch names) for an option argument
# specifying a new branch name. This is for
# convenience, assuming new branches are
# possibly based on pre-existing branch names.
__git_complete_refs $dwim_opt --mode="heads"
return
;;
*)
;;
esac
case "$cur" in
--conflict=*)
__gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
return
;;
--*)
__gitcomp_builtin checkout
return
;;
*)
# At this point, we've already handled special completion for
# the arguments to -b/-B, and --orphan. There are 3 main
# things left we can possibly complete:
# 1) a start-point for -b/-B, -d/--detach, or --orphan
# 2) a remote head, for --track
# 3) an arbitrary reference, possibly including DWIM names
#

case "$cur" in
--conflict=*)
__gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--*)
__gitcomp_builtin checkout
;;
*)
# At this point, we've already handled special completion for
# the arguments to -b/-B, and --orphan. There are 3 main
# things left we can possibly complete:
# 1) a start-point for -b/-B, -d/--detach, or --orphan
# 2) a remote head, for --track
# 3) an arbitrary reference, possibly including DWIM names
#
if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
__git_complete_refs --mode="refs"
elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
__git_complete_refs --mode="remote-heads"
else
__git_complete_refs $dwim_opt --mode="refs"
fi
;;
esac
fi

if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
__git_complete_refs --mode="refs"
elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
__git_complete_refs --mode="remote-heads"
else
__git_complete_refs $dwim_opt --mode="refs"
fi
;;
esac
if [ ${#COMPREPLY[@]} -eq 0 ]; then
__git_complete_index_file ""
fi

if [ ${#COMPREPLY[@]} -eq 0 ]; then
__git_complete_index_file "--others --directory"
fi
}

__git_sequencer_inprogress_options="--continue --quit --abort --skip"

View File

@ -2714,6 +2714,62 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
EOF
'

test_expect_success 'git checkout completes tracked paths when no refs match' '
# file1 and file2 are tracked but file3 is not
# there is no ref that begins with f
test_completion "git checkout f" <<-\EOF &&
file1
file2
EOF
test_completion "git checkout -- f" <<-\EOF
file1
file2
EOF
'

test_expect_success 'git checkout completes untracked paths, too' '
# ufile is not tracked and there is no ref that begins with u
test_completion "git checkout u" <<-\EOF &&
ufile
EOF
test_completion "git checkout -- u" <<-\EOF
ufile
EOF
'

test_expect_success 'git -C <path> checkout completes paths in specified repo' '
# otherfile is tracked, oops is not
# lostfile is tracked but lost, ufile is untracked.
test_when_finished "rm -rf repo-for-checkout" &&
git init repo-for-checkout &&
echo content >repo-for-checkout/otherfile &&
echo content >repo-for-checkout/lostfile &&
git -C repo-for-checkout add otherfile &&
git -C repo-for-checkout add lostfile &&
git -C repo-for-checkout commit -m otherfile &&
echo untracked >repo-for-checkout/oops &&
echo untracked >repo-for-checkout/ufile &&
rm -f repo-for-checkout/lostfile &&
test_completion "git -C repo-for-checkout checkout o" <<-\EOF &&
otherfile
EOF
test_completion "git -C repo-for-checkout checkout -- o" <<-\EOF &&
otherfile
EOF
test_completion "git -C repo-for-checkout checkout l" <<-\EOF &&
lostfile
EOF
test_completion "git -C repo-for-checkout checkout -- l" <<-\EOF &&
lostfile
EOF
test_completion "git -C repo-for-checkout checkout u" <<-\EOF &&
ufile
EOF
test_completion "git -C repo-for-checkout checkout -- u" <<-\EOF
ufile
EOF
'

test_expect_success 'git diff completes tracked paths when no refs match' '
# file1 and file2 are tracked but file3 is not
# there is no ref that begins with f