{"thread":{"id":"66150","subject":"[PATCH 0/2] completion of 'git [-C <dir>] checkout'","startedAt":"2026-08-11T03:19:35Z","lastAt":"2026-08-14T01:53:40Z","messageCount":19,"participants":["Junio C Hamano","Ben Knoble","Elijah Newren"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"550232","messageId":"xmqq7blx5oor.fsf@gitster.g","threadId":"66150","inReplyTo":null,"subject":"[PATCH 0/2] completion of 'git [-C <dir>] checkout'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T03:19:32Z","receivedAt":"2026-08-11T03:19:35Z","isPatch":true,"body":"Continuing from the previous 'git [-C <dir>] diff' series, these two\npatches correct completion for 'git checkout [-C <dir>] path<TAB>',\nbut without falling back to untracked paths, as \"checkout paths out\nof the index\" is by definition about paths that appear in the index.\n\n 1/2: completion: no-op refactoring of checkout completion\n 2/2: completion: complete tracked paths for \"git checkout\"\n\n contrib/completion/git-completion.bash | 86 ++++++++++++++------------\n t/t9902-completion.sh                  | 27 ++++++++\n 2 files changed, 73 insertions(+), 40 deletions(-)\n"},{"id":"550233","messageId":"xmqqse4l4a1v.fsf@gitster.g","threadId":"66150","inReplyTo":"xmqq7blx5oor.fsf@gitster.g","subject":"[PATCH 1/2] completion: no-op refactoring of checkout completion","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T03:21:00Z","receivedAt":"2026-08-11T03:21:02Z","isPatch":true,"body":"The 'git checkout' completion function punts very early when it sees\n'--' on the command line, as it indicates that options or revisions\ncan no longer appear.  By returning early, it allows the default\nBash action (which completes files in '$PWD') to kick in.\n\nIn preparation for changing what happens in the next step when\noption or revision completion yields no matching candidates, or when\n'--' is present, reorganize the control flow to avoid this early\nreturn, and add explicit returns to the option completion branches.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash | 84 +++++++++++++-------------\n 1 file changed, 43 insertions(+), 41 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex d35b4f3024..38dec1cabe 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1735,49 +1735,51 @@ __git_checkout_default_dwim_mode ()\n \n _git_checkout ()\n {\n-\t__git_has_doubledash && return\n-\n-\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n-\n-\tcase \"$prev\" in\n-\t-b|-B|--orphan)\n-\t\t# Complete local branches (and DWIM branch\n-\t\t# remote branch names) for an option argument\n-\t\t# specifying a new branch name. This is for\n-\t\t# convenience, assuming new branches are\n-\t\t# possibly based on pre-existing branch names.\n-\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n-\t\treturn\n-\t\t;;\n-\t*)\n-\t\t;;\n-\tesac\n+\tif ! __git_has_doubledash; then\n+\t\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n \n-\tcase \"$cur\" in\n-\t--conflict=*)\n-\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n-\t\t;;\n-\t--*)\n-\t\t__gitcomp_builtin checkout\n-\t\t;;\n-\t*)\n-\t\t# At this point, we've already handled special completion for\n-\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n-\t\t# things left we can possibly complete:\n-\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n-\t\t# 2) a remote head, for --track\n-\t\t# 3) an arbitrary reference, possibly including DWIM names\n-\t\t#\n+\t\tcase \"$prev\" in\n+\t\t-b|-B|--orphan)\n+\t\t\t# Complete local branches (and DWIM branch\n+\t\t\t# remote branch names) for an option argument\n+\t\t\t# specifying a new branch name. This is for\n+\t\t\t# convenience, assuming new branches are\n+\t\t\t# possibly based on pre-existing branch names.\n+\t\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t;;\n+\t\tesac\n \n-\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"refs\"\n-\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"remote-heads\"\n-\t\telse\n-\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n-\t\tfi\n-\t\t;;\n-\tesac\n+\t\tcase \"$cur\" in\n+\t\t--conflict=*)\n+\t\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t--*)\n+\t\t\t__gitcomp_builtin checkout\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t# At this point, we've already handled special completion for\n+\t\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n+\t\t\t# things left we can possibly complete:\n+\t\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n+\t\t\t# 2) a remote head, for --track\n+\t\t\t# 3) an arbitrary reference, possibly including DWIM names\n+\t\t\t#\n+\n+\t\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"refs\"\n+\t\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"remote-heads\"\n+\t\t\telse\n+\t\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n+\t\t\tfi\n+\t\t\t;;\n+\t\tesac\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\n-- \n2.55.0-698-g3e60a4dc4e\n\n\n"},{"id":"550234","messageId":"xmqqmrut4a1l.fsf@gitster.g","threadId":"66150","inReplyTo":"xmqq7blx5oor.fsf@gitster.g","subject":"[PATCH 2/2] completion: complete tracked paths for \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T03:21:10Z","receivedAt":"2026-08-11T03:21:12Z","isPatch":true,"body":"When completing arguments for \"git checkout\", _git_checkout()\ndelegates to __git_complete_refs(), which only completes revision\nreferences.  This is good, as mixing revisions and paths in a single\nlist from which the user can choose is confusing.  However, if no\nreference matches, or if \"--\" is given, _git_checkout() leaves\nCOMPREPLY empty.  Bash then falls back to the default filename\ncompletion in $PWD.\n\nThis fails when \"git -C <path>\" is used, as $PWD is not the target\nrepository.\n\nUpdate _git_checkout() to use __git_complete_index_file() when \"--\"\nis present, or when revision reference completion yields no matching\ncandidates, so that tracked paths are offered as candidates.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash |  4 ++++\n t/t9902-completion.sh                  | 27 ++++++++++++++++++++++++++\n 2 files changed, 31 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 38dec1cabe..bd4b6e9247 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1780,6 +1780,10 @@ _git_checkout ()\n \t\t\t;;\n \t\tesac\n \tfi\n+\n+\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n+\t\t__git_complete_index_file\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 53a2bfb2ac..46fe94d8d5 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2713,6 +2713,33 @@ test_expect_success 'git -C <path> checkout uses the right repo' '\n \tEOF\n '\n \n+test_expect_success 'git checkout completes tracked paths when no refs match' '\n+\t# file1 and file2 are tracked but ufile is not\n+\t# there is no ref that begins with f\n+\ttest_completion \"git checkout f\" <<-\\EOF &&\n+\tfile1\n+\tfile2\n+\tEOF\n+\ttest_completion \"git checkout -- f\" <<-\\EOF\n+\tfile1\n+\tfile2\n+\tEOF\n+'\n+\n+test_expect_success 'git -C <path> checkout completes tracked paths in specified repo' '\n+\ttest_when_finished \"rm -rf repo-for-checkout\" &&\n+\tgit init repo-for-checkout &&\n+\techo content >repo-for-checkout/otherfile &&\n+\tgit -C repo-for-checkout add otherfile &&\n+\tgit -C repo-for-checkout commit -m otherfile &&\n+\ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n+\totherfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- o\" <<-\\EOF\n+\totherfile\n+\tEOF\n+'\n+\n test_expect_success 'git diff completes tracked paths when no refs match' '\n \t# file1 and file2 are tracked but file3 is not\n \ttest_completion \"git diff f\" <<-\\EOF\n-- \n2.55.0-698-g3e60a4dc4e\n\n"},{"id":"550236","messageId":"xmqqcxvp481b.fsf@gitster.g","threadId":"66150","inReplyTo":"xmqq7blx5oor.fsf@gitster.g","subject":"Re: [PATCH 0/2] completion of 'git [-C <dir>] checkout'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T04:04:32Z","receivedAt":"2026-08-11T04:04:35Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Continuing from the previous 'git [-C <dir>] diff' series, these two\n> patches correct completion for 'git checkout [-C <dir>] path<TAB>',\n> but without falling back to untracked paths, as \"checkout paths out\n> of the index\" is by definition about paths that appear in the index.\n\nI suppose I will add the 'untracked, too' final fallback, just like\n'diff', because 'git checkout other-branch foo<TAB>' could resurrect\na path like 'foolish' that appears on 'other-branch' but not in the\ncurrently checked-out branch (and thus not in the index).  The\n'foolish' file might also appear in the working tree as an untracked\npath.\n\nThis is not particularly useful, however, as it is unlikely that a\nfile like 'foolish' tracked on another branch would be lying\naround untracked in the working tree to begin with.\n\nI did not, however, find any readily usable machinery in the\n'git-completion.bash' script that allows completing a path within an\narbitrary tree.  If such machinery were available, 'git checkout\nother-branch foo<TAB>' could capture the output of 'git ls-tree -r\nother-branch' and offer paths that begin with the given prefix.\n\nRegardless, implementing this is beyond my 'git-completion-fu' right\nnow.  As I mentioned, I barely managed the 'diff' completion as a\nmonkey-see-monkey-do patch series, and I would welcome others\nbuilding on top of this once the dust settles.\n\n\n>  1/2: completion: no-op refactoring of checkout completion\n>  2/2: completion: complete tracked paths for \"git checkout\"\n>\n>  contrib/completion/git-completion.bash | 86 ++++++++++++++------------\n>  t/t9902-completion.sh                  | 27 ++++++++\n>  2 files changed, 73 insertions(+), 40 deletions(-)\n"},{"id":"550298","messageId":"834FE315-FB34-47B7-9ACF-FC8194FBE8D5@gmail.com","threadId":"66150","inReplyTo":"xmqqmrut4a1l.fsf@gitster.g","subject":"Re: [PATCH 2/2] completion: complete tracked paths for \"git checkout\"","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-08-11T16:32:05Z","receivedAt":"2026-08-11T16:32:17Z","isPatch":true,"body":"\n> \n> Le 10 août 2026 à 23:21, Junio C Hamano <gitster@pobox.com> a écrit :\n> \n> ﻿When completing arguments for \"git checkout\", _git_checkout()\n> delegates to __git_complete_refs(), which only completes revision\n> references.  This is good, as mixing revisions and paths in a single\n> list from which the user can choose is confusing.  However, if no\n> reference matches, or if \"--\" is given, _git_checkout() leaves\n> COMPREPLY empty.  Bash then falls back to the default filename\n> completion in $PWD.\n> \n> This fails when \"git -C <path>\" is used, as $PWD is not the target\n> repository.\n> \n> Update _git_checkout() to use __git_complete_index_file() when \"--\"\n> is present, or when revision reference completion yields no matching\n> candidates, so that tracked paths are offered as candidates.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> contrib/completion/git-completion.bash |  4 ++++\n> t/t9902-completion.sh                  | 27 ++++++++++++++++++++++++++\n> 2 files changed, 31 insertions(+)\n> \n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 38dec1cabe..bd4b6e9247 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -1780,6 +1780,10 @@ _git_checkout ()\n>            ;;\n>        esac\n>    fi\n> +\n> +    if [ ${#COMPREPLY[@]} -eq 0 ]; then\n> +        __git_complete_index_file\n> +    fi\n> }\n\nAssuming the prior step was purely mechanical, which I did not validate, this looks reasonable to me. \n"},{"id":"550300","messageId":"B79E6047-CC44-42D0-A9CA-BF7B8D2C8B93@gmail.com","threadId":"66150","inReplyTo":"xmqqcxvp481b.fsf@gitster.g","subject":"Re: [PATCH 0/2] completion of 'git [-C <dir>] checkout'","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-08-11T16:33:53Z","receivedAt":"2026-08-11T16:34:05Z","isPatch":true,"body":"\n> Le 11 août 2026 à 00:04, Junio C Hamano <gitster@pobox.com> a écrit :\n> \n> ﻿Junio C Hamano <gitster@pobox.com> writes:\n> \n>> Continuing from the previous 'git [-C <dir>] diff' series, these two\n>> patches correct completion for 'git checkout [-C <dir>] path<TAB>',\n>> but without falling back to untracked paths, as \"checkout paths out\n>> of the index\" is by definition about paths that appear in the index.\n> \n> I suppose I will add the 'untracked, too' final fallback, just like\n> 'diff', because 'git checkout other-branch foo<TAB>' could resurrect\n> a path like 'foolish' that appears on 'other-branch' but not in the\n> currently checked-out branch (and thus not in the index).  The\n> 'foolish' file might also appear in the working tree as an untracked\n> path.\n> \n> This is not particularly useful, however, as it is unlikely that a\n> file like 'foolish' tracked on another branch would be lying\n> around untracked in the working tree to begin with.\n> \n> I did not, however, find any readily usable machinery in the\n> 'git-completion.bash' script that allows completing a path within an\n> arbitrary tree.  If such machinery were available, 'git checkout\n> other-branch foo<TAB>' could capture the output of 'git ls-tree -r\n> other-branch' and offer paths that begin with the given prefix.\n> \n> Regardless, implementing this is beyond my 'git-completion-fu' right\n> now.  As I mentioned, I barely managed the 'diff' completion as a\n> monkey-see-monkey-do patch series, and I would welcome others\n> building on top of this once the dust settles.\n\nI do not know where it lives (and it may be Zsh-specific) [partly because I haven’t looked while on mobile], but I think there is support for completing « path » in « git show <tree>:», so there might be something to build on there. "},{"id":"550305","messageId":"xmqqldac36z1.fsf@gitster.g","threadId":"66150","inReplyTo":"834FE315-FB34-47B7-9ACF-FC8194FBE8D5@gmail.com","subject":"Re: [PATCH 2/2] completion: complete tracked paths for \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T17:25:06Z","receivedAt":"2026-08-11T17:25:09Z","isPatch":true,"body":"Ben Knoble <ben.knoble@gmail.com> writes:\n\n>> \n>> Le 10 août 2026 à 23:21, Junio C Hamano <gitster@pobox.com> a écrit :\n>> \n>> ﻿When completing arguments for \"git checkout\", _git_checkout()\n>> delegates to __git_complete_refs(), which only completes revision\n>> references.  This is good, as mixing revisions and paths in a single\n>> list from which the user can choose is confusing.  However, if no\n>> reference matches, or if \"--\" is given, _git_checkout() leaves\n>> COMPREPLY empty.  Bash then falls back to the default filename\n>> completion in $PWD.\n>> \n>> This fails when \"git -C <path>\" is used, as $PWD is not the target\n>> repository.\n>> \n>> Update _git_checkout() to use __git_complete_index_file() when \"--\"\n>> is present, or when revision reference completion yields no matching\n>> candidates, so that tracked paths are offered as candidates.\n>> \n>> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n>> ---\n>> contrib/completion/git-completion.bash |  4 ++++\n>> t/t9902-completion.sh                  | 27 ++++++++++++++++++++++++++\n>> 2 files changed, 31 insertions(+)\n>> \n>> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n>> index 38dec1cabe..bd4b6e9247 100644\n>> --- a/contrib/completion/git-completion.bash\n>> +++ b/contrib/completion/git-completion.bash\n>> @@ -1780,6 +1780,10 @@ _git_checkout ()\n>>            ;;\n>>        esac\n>>    fi\n>> +\n>> +    if [ ${#COMPREPLY[@]} -eq 0 ]; then\n>> +        __git_complete_index_file\n>> +    fi\n>> }\n>\n> Assuming the prior step was purely mechanical, which I did not validate, this looks reasonable to me. \n\nActually the new call should use \"--cached\" to complete from the\npaths in the index, I think.\n\n"},{"id":"550337","messageId":"xmqqpkzoyuuh.fsf@gitster.g","threadId":"66150","inReplyTo":"B79E6047-CC44-42D0-A9CA-BF7B8D2C8B93@gmail.com","subject":"Re: [PATCH 0/2] completion of 'git [-C <dir>] checkout'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T01:45:58Z","receivedAt":"2026-08-12T01:46:02Z","isPatch":true,"body":"Ben Knoble <ben.knoble@gmail.com> writes:\n\n>> Le 11 août 2026 à 00:04, Junio C Hamano <gitster@pobox.com> a écrit :\n>> \n>> I did not, however, find any readily usable machinery in the\n>> 'git-completion.bash' script that allows completing a path within an\n>> arbitrary tree.  If such machinery were available, 'git checkout\n>> other-branch foo<TAB>' could capture the output of 'git ls-tree -r\n>> other-branch' and offer paths that begin with the given prefix.\n>> \n>> Regardless, implementing this is beyond my 'git-completion-fu' right\n>> now.  As I mentioned, I barely managed the 'diff' completion as a\n>> monkey-see-monkey-do patch series, and I would welcome others\n>> building on top of this once the dust settles.\n>\n> I do not know where it lives (and it may be Zsh-specific) [partly\n> because I haven’t looked while on mobile], but I think there is\n> support for completing « path » in « git show <tree>:», so there\n> might be something to build on there.\n\nI know where treeish:path<TAB> support is.  The thing is, it is not\na reusable machinery for \"git checkout treeish path<TAB>\".\n\nThanks.\n"},{"id":"550423","messageId":"20260812164846.2236221-1-gitster@pobox.com","threadId":"66150","inReplyTo":"xmqq7blx5oor.fsf@gitster.g","subject":"[PATCH v2 0/3] completion of 'git [-C <dir>] checkout'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T16:48:43Z","receivedAt":"2026-08-12T16:48:52Z","isPatch":true,"body":"Continuing from the previous 'git [-C <dir>] diff' series, these two\npatches correct completion for 'git checkout [-C <dir>] path<TAB>',\nbut without falling back to untracked paths, as \"checkout paths out\nof the index\" is by definition about paths that appear in the index.\n\nThis has been rebuilt on the v6 iteration of the 'complete-diff'\ntopic to give the tests a more structured and logical organization.\n\nAdditionally, [PATCH 3/3] is new.  It teaches the completion\nmachinery to complete untracked paths in the working tree, which may\nbe of limited use, as it is unlikely that a file tracked on another\nbranch (and thus capable of being restored with 'git checkout\nanother-branch path') would be lying around untracked in the working\ntree to begin with.\n\n 1/3: completion: no-op refactoring of checkout completion\n 2/3: completion: complete tracked paths for \"git checkout\"\n 3/3: completion: 'git checkout' completes untracked paths as a last\n        resort\n\n contrib/completion/git-completion.bash | 88 ++++++++++++++------------\n t/t9902-completion.sh                  | 56 ++++++++++++++++\n 2 files changed, 105 insertions(+), 39 deletions(-)\n\nRange-diff against v1:\n1:  f35017f5c9 = 1:  d3b022ac73 completion: no-op refactoring of checkout completion\n2:  20d57f0336 ! 2:  85019fbf97 completion: complete tracked paths for \"git checkout\"\n    @@ contrib/completion/git-completion.bash: _git_checkout ()\n      \tfi\n     +\n     +\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n    -+\t\t__git_complete_index_file\n    ++\t\t__git_complete_index_file \"\"\n     +\tfi\n      }\n      \n    @@ t/t9902-completion.sh: test_expect_success 'git -C <path> checkout uses the righ\n      '\n      \n     +test_expect_success 'git checkout completes tracked paths when no refs match' '\n    -+\t# file1 and file2 are tracked but ufile is not\n    ++\t# file1 and file2 are tracked but file3 is not\n     +\t# there is no ref that begins with f\n     +\ttest_completion \"git checkout f\" <<-\\EOF &&\n     +\tfile1\n    @@ t/t9902-completion.sh: test_expect_success 'git -C <path> checkout uses the righ\n     +\tEOF\n     +'\n     +\n    -+test_expect_success 'git -C <path> checkout completes tracked paths in specified repo' '\n    ++test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n    ++\t# otherfile is tracked, oops is not\n    ++\t# lostfile is tracked but lost\n     +\ttest_when_finished \"rm -rf repo-for-checkout\" &&\n     +\tgit init repo-for-checkout &&\n     +\techo content >repo-for-checkout/otherfile &&\n    ++\techo content >repo-for-checkout/lostfile &&\n     +\tgit -C repo-for-checkout add otherfile &&\n    ++\tgit -C repo-for-checkout add lostfile &&\n     +\tgit -C repo-for-checkout commit -m otherfile &&\n    ++\techo untracked >repo-for-checkout/oops &&\n    ++\trm -f repo-for-checkout/lostfile &&\n     +\ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n     +\totherfile\n     +\tEOF\n    -+\ttest_completion \"git -C repo-for-checkout checkout -- o\" <<-\\EOF\n    ++\ttest_completion \"git -C repo-for-checkout checkout -- o\" <<-\\EOF &&\n     +\totherfile\n     +\tEOF\n    ++\ttest_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n    ++\tlostfile\n    ++\tEOF\n    ++\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n    ++\tlostfile\n    ++\tEOF\n     +'\n     +\n      test_expect_success 'git diff completes tracked paths when no refs match' '\n-:  ---------- > 3:  56aade8759 completion: 'git checkout' completes untracked paths as a last resort\n"},{"id":"550424","messageId":"20260812164846.2236221-2-gitster@pobox.com","threadId":"66150","inReplyTo":"20260812164846.2236221-1-gitster@pobox.com","subject":"[PATCH v2 1/3] completion: no-op refactoring of checkout completion","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T16:48:44Z","receivedAt":"2026-08-12T16:48:52Z","isPatch":true,"body":"The 'git checkout' completion function punts very early when it sees\n'--' on the command line, as it indicates that options or revisions\ncan no longer appear.  By returning early, it allows the default\nBash action (which completes files in '$PWD') to kick in.\n\nIn preparation for changing what happens in the next step when\noption or revision completion yields no matching candidates, or when\n'--' is present, reorganize the control flow to avoid this early\nreturn, and add explicit returns to the option completion branches.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash | 84 +++++++++++++-------------\n 1 file changed, 43 insertions(+), 41 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex d35b4f3024..38dec1cabe 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1735,49 +1735,51 @@ __git_checkout_default_dwim_mode ()\n \n _git_checkout ()\n {\n-\t__git_has_doubledash && return\n-\n-\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n-\n-\tcase \"$prev\" in\n-\t-b|-B|--orphan)\n-\t\t# Complete local branches (and DWIM branch\n-\t\t# remote branch names) for an option argument\n-\t\t# specifying a new branch name. This is for\n-\t\t# convenience, assuming new branches are\n-\t\t# possibly based on pre-existing branch names.\n-\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n-\t\treturn\n-\t\t;;\n-\t*)\n-\t\t;;\n-\tesac\n+\tif ! __git_has_doubledash; then\n+\t\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n \n-\tcase \"$cur\" in\n-\t--conflict=*)\n-\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n-\t\t;;\n-\t--*)\n-\t\t__gitcomp_builtin checkout\n-\t\t;;\n-\t*)\n-\t\t# At this point, we've already handled special completion for\n-\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n-\t\t# things left we can possibly complete:\n-\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n-\t\t# 2) a remote head, for --track\n-\t\t# 3) an arbitrary reference, possibly including DWIM names\n-\t\t#\n+\t\tcase \"$prev\" in\n+\t\t-b|-B|--orphan)\n+\t\t\t# Complete local branches (and DWIM branch\n+\t\t\t# remote branch names) for an option argument\n+\t\t\t# specifying a new branch name. This is for\n+\t\t\t# convenience, assuming new branches are\n+\t\t\t# possibly based on pre-existing branch names.\n+\t\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t;;\n+\t\tesac\n \n-\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"refs\"\n-\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"remote-heads\"\n-\t\telse\n-\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n-\t\tfi\n-\t\t;;\n-\tesac\n+\t\tcase \"$cur\" in\n+\t\t--conflict=*)\n+\t\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t--*)\n+\t\t\t__gitcomp_builtin checkout\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t# At this point, we've already handled special completion for\n+\t\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n+\t\t\t# things left we can possibly complete:\n+\t\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n+\t\t\t# 2) a remote head, for --track\n+\t\t\t# 3) an arbitrary reference, possibly including DWIM names\n+\t\t\t#\n+\n+\t\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"refs\"\n+\t\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"remote-heads\"\n+\t\t\telse\n+\t\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n+\t\t\tfi\n+\t\t\t;;\n+\t\tesac\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\n-- \n2.55.0-721-g26b8014fc4\n\n"},{"id":"550425","messageId":"20260812164846.2236221-3-gitster@pobox.com","threadId":"66150","inReplyTo":"20260812164846.2236221-1-gitster@pobox.com","subject":"[PATCH v2 2/3] completion: complete tracked paths for \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T16:48:45Z","receivedAt":"2026-08-12T16:48:54Z","isPatch":true,"body":"When completing arguments for \"git checkout\", _git_checkout()\ndelegates to __git_complete_refs(), which only completes revision\nreferences.  This is good, as mixing revisions and paths in a single\nlist from which the user can choose is confusing.  However, if no\nreference matches, or if \"--\" is given, _git_checkout() leaves\nCOMPREPLY empty.  Bash then falls back to the default filename\ncompletion in $PWD.\n\nThis fails when \"git -C <path>\" is used, as $PWD is not the target\nrepository.\n\nUpdate _git_checkout() to use __git_complete_index_file() when \"--\"\nis present, or when revision reference completion yields no matching\ncandidates, so that tracked paths are offered as candidates.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash |  4 +++\n t/t9902-completion.sh                  | 39 ++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 38dec1cabe..0eecfcbf8b 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1780,6 +1780,10 @@ _git_checkout ()\n \t\t\t;;\n \t\tesac\n \tfi\n+\n+\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n+\t\t__git_complete_index_file \"\"\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex b889ec8c77..13fa5c65c3 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2714,6 +2714,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '\n \tEOF\n '\n \n+test_expect_success 'git checkout completes tracked paths when no refs match' '\n+\t# file1 and file2 are tracked but file3 is not\n+\t# there is no ref that begins with f\n+\ttest_completion \"git checkout f\" <<-\\EOF &&\n+\tfile1\n+\tfile2\n+\tEOF\n+\ttest_completion \"git checkout -- f\" <<-\\EOF\n+\tfile1\n+\tfile2\n+\tEOF\n+'\n+\n+test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n+\t# otherfile is tracked, oops is not\n+\t# lostfile is tracked but lost\n+\ttest_when_finished \"rm -rf repo-for-checkout\" &&\n+\tgit init repo-for-checkout &&\n+\techo content >repo-for-checkout/otherfile &&\n+\techo content >repo-for-checkout/lostfile &&\n+\tgit -C repo-for-checkout add otherfile &&\n+\tgit -C repo-for-checkout add lostfile &&\n+\tgit -C repo-for-checkout commit -m otherfile &&\n+\techo untracked >repo-for-checkout/oops &&\n+\trm -f repo-for-checkout/lostfile &&\n+\ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n+\totherfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- o\" <<-\\EOF &&\n+\totherfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n+\tlostfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n+\tlostfile\n+\tEOF\n+'\n+\n test_expect_success 'git diff completes tracked paths when no refs match' '\n \t# file1 and file2 are tracked but file3 is not\n \t# there is no ref that begins with f\n-- \n2.55.0-721-g26b8014fc4\n\n"},{"id":"550426","messageId":"20260812164846.2236221-4-gitster@pobox.com","threadId":"66150","inReplyTo":"20260812164846.2236221-1-gitster@pobox.com","subject":"[PATCH v2 3/3] completion: 'git checkout' completes untracked paths as a last resort","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T16:48:46Z","receivedAt":"2026-08-12T16:48:55Z","isPatch":true,"body":"We taught 'git checkout' to first try to complete revisions (unless\n'--' is present on the command line) and, failing that, to complete\ntracked paths.  If this yields nothing, it lets the Bash default,\nwhich offers paths in $PWD, kick in.\n\nTeach it to complete untracked paths before giving up and letting\nthe Bash default kick in.  With this change,\n\n    $ git -C another-directory checkout un<TAB>\n\nfinds the 'untracked' file in another-directory and offers it as a\ncompletion candidate.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash |  4 ++++\n t/t9902-completion.sh                  | 21 +++++++++++++++++++--\n 2 files changed, 23 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 0eecfcbf8b..22c53697ab 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1784,6 +1784,10 @@ _git_checkout ()\n \tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n \t\t__git_complete_index_file \"\"\n \tfi\n+\n+\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n+\t\t__git_complete_index_file \"--others\"\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 13fa5c65c3..e8418f069b 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2727,9 +2727,19 @@ test_expect_success 'git checkout completes tracked paths when no refs match' '\n \tEOF\n '\n \n+test_expect_success 'git checkout completes untracked paths, too' '\n+\t# ufile is not tracked and there is no ref that begins with u\n+\ttest_completion \"git checkout u\" <<-\\EOF &&\n+\tufile\n+\tEOF\n+\ttest_completion \"git checkout -- u\" <<-\\EOF\n+\tufile\n+\tEOF\n+'\n+\n test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \t# otherfile is tracked, oops is not\n-\t# lostfile is tracked but lost\n+\t# lostfile is tracked but lost, ufile is untracked.\n \ttest_when_finished \"rm -rf repo-for-checkout\" &&\n \tgit init repo-for-checkout &&\n \techo content >repo-for-checkout/otherfile &&\n@@ -2738,6 +2748,7 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \tgit -C repo-for-checkout add lostfile &&\n \tgit -C repo-for-checkout commit -m otherfile &&\n \techo untracked >repo-for-checkout/oops &&\n+\techo untracked >repo-for-checkout/ufile &&\n \trm -f repo-for-checkout/lostfile &&\n \ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n \totherfile\n@@ -2748,9 +2759,15 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \ttest_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n \tlostfile\n \tEOF\n-\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF &&\n \tlostfile\n \tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout u\" <<-\\EOF &&\n+\tufile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- u\" <<-\\EOF\n+\tufile\n+\tEOF\n '\n \n test_expect_success 'git diff completes tracked paths when no refs match' '\n-- \n2.55.0-721-g26b8014fc4\n\n"},{"id":"550448","messageId":"CABPp-BFv4uf0F1PxVYRuQDbJQWw-YFDNbr1QRUVZxadsexbu5A@mail.gmail.com","threadId":"66150","inReplyTo":"20260812164846.2236221-4-gitster@pobox.com","subject":"Re: [PATCH v2 3/3] completion: 'git checkout' completes untracked paths as a last resort","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2026-08-12T19:57:10Z","receivedAt":"2026-08-12T19:57:27Z","isPatch":true,"body":"On Wed, Aug 12, 2026 at 9:49 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> We taught 'git checkout' to first try to complete revisions (unless\n> '--' is present on the command line) and, failing that, to complete\n> tracked paths.  If this yields nothing, it lets the Bash default,\n> which offers paths in $PWD, kick in.\n>\n> Teach it to complete untracked paths before giving up and letting\n> the Bash default kick in.  With this change,\n>\n>     $ git -C another-directory checkout un<TAB>\n>\n> finds the 'untracked' file in another-directory and offers it as a\n> completion candidate.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  contrib/completion/git-completion.bash |  4 ++++\n>  t/t9902-completion.sh                  | 21 +++++++++++++++++++--\n>  2 files changed, 23 insertions(+), 2 deletions(-)\n>\n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 0eecfcbf8b..22c53697ab 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -1784,6 +1784,10 @@ _git_checkout ()\n>         if [ ${#COMPREPLY[@]} -eq 0 ]; then\n>                 __git_complete_index_file \"\"\n>         fi\n> +\n> +       if [ ${#COMPREPLY[@]} -eq 0 ]; then\n> +               __git_complete_index_file \"--others\"\n> +       fi\n\nIn your completion series for `git diff`, you passed \"--others\n--directory\", but you omit --directory here.\n\nI think playing around with it for a while that it works either way,\nbecause without --directory __git_index_files splits on \"/\" and only\nkeeps the leading component, but not passing --directory means it'll\njust have to parse through and then compress more options.\n\nIt's probably not a big deal either way, but might be nice to include\n\"--directory\" here for consistency with your diff series, and because\nit'll be a bit more performant.\n\n>  }\n>\n>  __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\n> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\n> index 13fa5c65c3..e8418f069b 100755\n> --- a/t/t9902-completion.sh\n> +++ b/t/t9902-completion.sh\n> @@ -2727,9 +2727,19 @@ test_expect_success 'git checkout completes tracked paths when no refs match' '\n>         EOF\n>  '\n>\n> +test_expect_success 'git checkout completes untracked paths, too' '\n> +       # ufile is not tracked and there is no ref that begins with u\n> +       test_completion \"git checkout u\" <<-\\EOF &&\n> +       ufile\n> +       EOF\n> +       test_completion \"git checkout -- u\" <<-\\EOF\n> +       ufile\n> +       EOF\n> +'\n> +\n>  test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n>         # otherfile is tracked, oops is not\n> -       # lostfile is tracked but lost\n> +       # lostfile is tracked but lost, ufile is untracked.\n>         test_when_finished \"rm -rf repo-for-checkout\" &&\n>         git init repo-for-checkout &&\n>         echo content >repo-for-checkout/otherfile &&\n> @@ -2738,6 +2748,7 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n>         git -C repo-for-checkout add lostfile &&\n>         git -C repo-for-checkout commit -m otherfile &&\n>         echo untracked >repo-for-checkout/oops &&\n> +       echo untracked >repo-for-checkout/ufile &&\n>         rm -f repo-for-checkout/lostfile &&\n>         test_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n>         otherfile\n> @@ -2748,9 +2759,15 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n>         test_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n>         lostfile\n>         EOF\n> -       test_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n> +       test_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF &&\n>         lostfile\n>         EOF\n> +       test_completion \"git -C repo-for-checkout checkout u\" <<-\\EOF &&\n> +       ufile\n> +       EOF\n> +       test_completion \"git -C repo-for-checkout checkout -- u\" <<-\\EOF\n> +       ufile\n> +       EOF\n>  '\n>\n>  test_expect_success 'git diff completes tracked paths when no refs match' '\n> --\n\nLooks fine otherwise (as do the first two patches).\n"},{"id":"550450","messageId":"xmqq7blvw0ro.fsf@gitster.g","threadId":"66150","inReplyTo":"CABPp-BFv4uf0F1PxVYRuQDbJQWw-YFDNbr1QRUVZxadsexbu5A@mail.gmail.com","subject":"Re: [PATCH v2 3/3] completion: 'git checkout' completes untracked paths as a last resort","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T20:18:35Z","receivedAt":"2026-08-12T20:18:37Z","isPatch":true,"body":"Elijah Newren <newren@gmail.com> writes:\n\n> It's probably not a big deal either way, but might be nice to include\n> \"--directory\" here for consistency with your diff series, and because\n> it'll be a bit more performant.\n\nThanks, will do.\n\n"},{"id":"550561","messageId":"20260813191234.1066662-1-gitster@pobox.com","threadId":"66150","inReplyTo":"xmqq7blx5oor.fsf@gitster.g","subject":"[PATCH v3 0/3] completion of 'git [-C <dir>] checkout'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-13T19:12:31Z","receivedAt":"2026-08-13T19:12:37Z","isPatch":true,"body":"Continuing from the previous 'git [-C <dir>] diff' series, these\npatches correct completion for 'git checkout [-C <dir>] path<TAB>',\nwith a fallback to complete untracked paths of somewhat dubious\nvalue, primarily as a reminder for future work.\n\nHopefully this will be the small and final update to the series.\n\n 1/3: completion: no-op refactoring of checkout completion\n 2/3: completion: complete tracked paths for \"git checkout\"\n 3/3: completion: 'git checkout' completes untracked paths as a last\n        resort\n\n contrib/completion/git-completion.bash | 88 ++++++++++++++------------\n t/t9902-completion.sh                  | 56 ++++++++++++++++\n 2 files changed, 105 insertions(+), 39 deletions(-)\n\nRange-diff against v2:\n1:  33d136ffde = 1:  33d136ffde completion: no-op refactoring of checkout completion\n2:  a28754cfd9 = 2:  a28754cfd9 completion: complete tracked paths for \"git checkout\"\n3:  90aec6539c ! 3:  17880068c0 completion: 'git checkout' completes untracked paths as a last resort\n    @@ contrib/completion/git-completion.bash: _git_checkout ()\n      \tfi\n     +\n     +\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n    -+\t\t__git_complete_index_file \"--others\"\n    ++\t\t__git_complete_index_file \"--others --directory\"\n     +\tfi\n      }\n      \n-- \n2.55.0-759-g9dcc51a0fd\n\n"},{"id":"550562","messageId":"20260813191234.1066662-2-gitster@pobox.com","threadId":"66150","inReplyTo":"20260813191234.1066662-1-gitster@pobox.com","subject":"[PATCH v3 1/3] completion: no-op refactoring of checkout completion","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-13T19:12:32Z","receivedAt":"2026-08-13T19:12:38Z","isPatch":true,"body":"The 'git checkout' completion function punts very early when it sees\n'--' on the command line, as it indicates that options or revisions\ncan no longer appear.  By returning early, it allows the default\nBash action (which completes files in '$PWD') to kick in.\n\nIn preparation for changing what happens in the next step when\noption or revision completion yields no matching candidates, or when\n'--' is present, reorganize the control flow to avoid this early\nreturn, and add explicit returns to the option completion branches.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash | 84 +++++++++++++-------------\n 1 file changed, 43 insertions(+), 41 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex d35b4f3024..38dec1cabe 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1735,49 +1735,51 @@ __git_checkout_default_dwim_mode ()\n \n _git_checkout ()\n {\n-\t__git_has_doubledash && return\n-\n-\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n-\n-\tcase \"$prev\" in\n-\t-b|-B|--orphan)\n-\t\t# Complete local branches (and DWIM branch\n-\t\t# remote branch names) for an option argument\n-\t\t# specifying a new branch name. This is for\n-\t\t# convenience, assuming new branches are\n-\t\t# possibly based on pre-existing branch names.\n-\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n-\t\treturn\n-\t\t;;\n-\t*)\n-\t\t;;\n-\tesac\n+\tif ! __git_has_doubledash; then\n+\t\tlocal dwim_opt=\"$(__git_checkout_default_dwim_mode)\"\n \n-\tcase \"$cur\" in\n-\t--conflict=*)\n-\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n-\t\t;;\n-\t--*)\n-\t\t__gitcomp_builtin checkout\n-\t\t;;\n-\t*)\n-\t\t# At this point, we've already handled special completion for\n-\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n-\t\t# things left we can possibly complete:\n-\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n-\t\t# 2) a remote head, for --track\n-\t\t# 3) an arbitrary reference, possibly including DWIM names\n-\t\t#\n+\t\tcase \"$prev\" in\n+\t\t-b|-B|--orphan)\n+\t\t\t# Complete local branches (and DWIM branch\n+\t\t\t# remote branch names) for an option argument\n+\t\t\t# specifying a new branch name. This is for\n+\t\t\t# convenience, assuming new branches are\n+\t\t\t# possibly based on pre-existing branch names.\n+\t\t\t__git_complete_refs $dwim_opt --mode=\"heads\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t;;\n+\t\tesac\n \n-\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"refs\"\n-\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n-\t\t\t__git_complete_refs --mode=\"remote-heads\"\n-\t\telse\n-\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n-\t\tfi\n-\t\t;;\n-\tesac\n+\t\tcase \"$cur\" in\n+\t\t--conflict=*)\n+\t\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n+\t\t\treturn\n+\t\t\t;;\n+\t\t--*)\n+\t\t\t__gitcomp_builtin checkout\n+\t\t\treturn\n+\t\t\t;;\n+\t\t*)\n+\t\t\t# At this point, we've already handled special completion for\n+\t\t\t# the arguments to -b/-B, and --orphan. There are 3 main\n+\t\t\t# things left we can possibly complete:\n+\t\t\t# 1) a start-point for -b/-B, -d/--detach, or --orphan\n+\t\t\t# 2) a remote head, for --track\n+\t\t\t# 3) an arbitrary reference, possibly including DWIM names\n+\t\t\t#\n+\n+\t\t\tif [ -n \"$(__git_find_on_cmdline \"-b -B -d --detach --orphan\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"refs\"\n+\t\t\telif [ -n \"$(__git_find_on_cmdline \"-t --track\")\" ]; then\n+\t\t\t\t__git_complete_refs --mode=\"remote-heads\"\n+\t\t\telse\n+\t\t\t\t__git_complete_refs $dwim_opt --mode=\"refs\"\n+\t\t\tfi\n+\t\t\t;;\n+\t\tesac\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\n-- \n2.55.0-759-g9dcc51a0fd\n\n"},{"id":"550563","messageId":"20260813191234.1066662-3-gitster@pobox.com","threadId":"66150","inReplyTo":"20260813191234.1066662-1-gitster@pobox.com","subject":"[PATCH v3 2/3] completion: complete tracked paths for \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-13T19:12:33Z","receivedAt":"2026-08-13T19:12:40Z","isPatch":true,"body":"When completing arguments for \"git checkout\", _git_checkout()\ndelegates to __git_complete_refs(), which only completes revision\nreferences.  This is good, as mixing revisions and paths in a single\nlist from which the user can choose is confusing.  However, if no\nreference matches, or if \"--\" is given, _git_checkout() leaves\nCOMPREPLY empty.  Bash then falls back to the default filename\ncompletion in $PWD.\n\nThis fails when \"git -C <path>\" is used, as $PWD is not the target\nrepository.\n\nUpdate _git_checkout() to use __git_complete_index_file() when \"--\"\nis present, or when revision reference completion yields no matching\ncandidates, so that tracked paths are offered as candidates.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash |  4 +++\n t/t9902-completion.sh                  | 39 ++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 38dec1cabe..0eecfcbf8b 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1780,6 +1780,10 @@ _git_checkout ()\n \t\t\t;;\n \t\tesac\n \tfi\n+\n+\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n+\t\t__git_complete_index_file \"\"\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex b889ec8c77..13fa5c65c3 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2714,6 +2714,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '\n \tEOF\n '\n \n+test_expect_success 'git checkout completes tracked paths when no refs match' '\n+\t# file1 and file2 are tracked but file3 is not\n+\t# there is no ref that begins with f\n+\ttest_completion \"git checkout f\" <<-\\EOF &&\n+\tfile1\n+\tfile2\n+\tEOF\n+\ttest_completion \"git checkout -- f\" <<-\\EOF\n+\tfile1\n+\tfile2\n+\tEOF\n+'\n+\n+test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n+\t# otherfile is tracked, oops is not\n+\t# lostfile is tracked but lost\n+\ttest_when_finished \"rm -rf repo-for-checkout\" &&\n+\tgit init repo-for-checkout &&\n+\techo content >repo-for-checkout/otherfile &&\n+\techo content >repo-for-checkout/lostfile &&\n+\tgit -C repo-for-checkout add otherfile &&\n+\tgit -C repo-for-checkout add lostfile &&\n+\tgit -C repo-for-checkout commit -m otherfile &&\n+\techo untracked >repo-for-checkout/oops &&\n+\trm -f repo-for-checkout/lostfile &&\n+\ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n+\totherfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- o\" <<-\\EOF &&\n+\totherfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n+\tlostfile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n+\tlostfile\n+\tEOF\n+'\n+\n test_expect_success 'git diff completes tracked paths when no refs match' '\n \t# file1 and file2 are tracked but file3 is not\n \t# there is no ref that begins with f\n-- \n2.55.0-759-g9dcc51a0fd\n\n"},{"id":"550564","messageId":"20260813191234.1066662-4-gitster@pobox.com","threadId":"66150","inReplyTo":"20260813191234.1066662-1-gitster@pobox.com","subject":"[PATCH v3 3/3] completion: 'git checkout' completes untracked paths as a last resort","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-13T19:12:34Z","receivedAt":"2026-08-13T19:12:42Z","isPatch":true,"body":"We taught 'git checkout' to first try to complete revisions (unless\n'--' is present on the command line) and, failing that, to complete\ntracked paths.  If this yields nothing, it lets the Bash default,\nwhich offers paths in $PWD, kick in.\n\nTeach it to complete untracked paths before giving up and letting\nthe Bash default kick in.  With this change,\n\n    $ git -C another-directory checkout un<TAB>\n\nfinds the 'untracked' file in another-directory and offers it as a\ncompletion candidate.\n\nNote that this is of somewhat dubious value, as an untracked path by\ndefinition does not exist in the index, so checking it out from the\nindex would not work well.  Even when used to check out the path\nfrom a different branch, it is still of dubious value because it is\nunlikely that a path tracked in another branch is lying untracked in\nthe working tree, as switching from a branch with the path to a\nbranch without it will normally remove the file in the working tree.\n\nA better behavior probably is to detect the tree-ish argument on\nthe command line and offer paths with the given prefix as candidates,\nbut there is no __git_complete_from_tree() helper readily usable,\nso mark this as #leftoverbits to wait for another day.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n contrib/completion/git-completion.bash |  4 ++++\n t/t9902-completion.sh                  | 21 +++++++++++++++++++--\n 2 files changed, 23 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 0eecfcbf8b..e6dce62d3c 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1784,6 +1784,10 @@ _git_checkout ()\n \tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n \t\t__git_complete_index_file \"\"\n \tfi\n+\n+\tif [ ${#COMPREPLY[@]} -eq 0 ]; then\n+\t\t__git_complete_index_file \"--others --directory\"\n+\tfi\n }\n \n __git_sequencer_inprogress_options=\"--continue --quit --abort --skip\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 13fa5c65c3..e8418f069b 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2727,9 +2727,19 @@ test_expect_success 'git checkout completes tracked paths when no refs match' '\n \tEOF\n '\n \n+test_expect_success 'git checkout completes untracked paths, too' '\n+\t# ufile is not tracked and there is no ref that begins with u\n+\ttest_completion \"git checkout u\" <<-\\EOF &&\n+\tufile\n+\tEOF\n+\ttest_completion \"git checkout -- u\" <<-\\EOF\n+\tufile\n+\tEOF\n+'\n+\n test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \t# otherfile is tracked, oops is not\n-\t# lostfile is tracked but lost\n+\t# lostfile is tracked but lost, ufile is untracked.\n \ttest_when_finished \"rm -rf repo-for-checkout\" &&\n \tgit init repo-for-checkout &&\n \techo content >repo-for-checkout/otherfile &&\n@@ -2738,6 +2748,7 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \tgit -C repo-for-checkout add lostfile &&\n \tgit -C repo-for-checkout commit -m otherfile &&\n \techo untracked >repo-for-checkout/oops &&\n+\techo untracked >repo-for-checkout/ufile &&\n \trm -f repo-for-checkout/lostfile &&\n \ttest_completion \"git -C repo-for-checkout checkout o\" <<-\\EOF &&\n \totherfile\n@@ -2748,9 +2759,15 @@ test_expect_success 'git -C <path> checkout completes paths in specified repo' '\n \ttest_completion \"git -C repo-for-checkout checkout l\" <<-\\EOF &&\n \tlostfile\n \tEOF\n-\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- l\" <<-\\EOF &&\n \tlostfile\n \tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout u\" <<-\\EOF &&\n+\tufile\n+\tEOF\n+\ttest_completion \"git -C repo-for-checkout checkout -- u\" <<-\\EOF\n+\tufile\n+\tEOF\n '\n \n test_expect_success 'git diff completes tracked paths when no refs match' '\n-- \n2.55.0-759-g9dcc51a0fd\n\n"},{"id":"550586","messageId":"CABPp-BFeLStBR3OeTCJoBmC7cn_VXrn4RcvBg-WWGyz4LpJxsg@mail.gmail.com","threadId":"66150","inReplyTo":"20260813191234.1066662-1-gitster@pobox.com","subject":"Re: [PATCH v3 0/3] completion of 'git [-C <dir>] checkout'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2026-08-14T01:53:27Z","receivedAt":"2026-08-14T01:53:40Z","isPatch":true,"body":"On Thu, Aug 13, 2026 at 12:12 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Continuing from the previous 'git [-C <dir>] diff' series, these\n> patches correct completion for 'git checkout [-C <dir>] path<TAB>',\n> with a fallback to complete untracked paths of somewhat dubious\n> value, primarily as a reminder for future work.\n>\n> Hopefully this will be the small and final update to the series.\n>\n>  1/3: completion: no-op refactoring of checkout completion\n>  2/3: completion: complete tracked paths for \"git checkout\"\n>  3/3: completion: 'git checkout' completes untracked paths as a last\n>         resort\n>\n>  contrib/completion/git-completion.bash | 88 ++++++++++++++------------\n>  t/t9902-completion.sh                  | 56 ++++++++++++++++\n>  2 files changed, 105 insertions(+), 39 deletions(-)\n>\n> Range-diff against v2:\n> 1:  33d136ffde = 1:  33d136ffde completion: no-op refactoring of checkout completion\n> 2:  a28754cfd9 = 2:  a28754cfd9 completion: complete tracked paths for \"git checkout\"\n> 3:  90aec6539c ! 3:  17880068c0 completion: 'git checkout' completes untracked paths as a last resort\n>     @@ contrib/completion/git-completion.bash: _git_checkout ()\n>         fi\n>      +\n>      +  if [ ${#COMPREPLY[@]} -eq 0 ]; then\n>     -+          __git_complete_index_file \"--others\"\n>     ++          __git_complete_index_file \"--others --directory\"\n>      +  fi\n>       }\n>\n> --\n> 2.55.0-759-g9dcc51a0fd\n\nThis version looks good to me; thanks!\n"}]}