* Re: [PATCH] completion: complete tracked paths for 'git diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
@ 2026-08-03 1:07 ` Junio C Hamano
2026-08-03 5:44 ` SZEDER Gábor
` (4 subsequent siblings)
5 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-03 1:07 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt
Junio C Hamano <gitster@pobox.com> writes:
> When completing arguments for 'git diff', _git_diff() delegates to
> __git_complete_revlist_file(), which only completes revision
> references. This is good [*], as mixing both revs and paths in a
> single list to have the user pick is simply too confusing.
>
> If no reference matches, or if '--' is given, however, _git_diff()
> leaves COMPREPLY empty. Bash then falls back to default filename
> completion in $PWD. This fails when 'git -C <path>' is used because
> $PWD is not the target repository.
>
> Update _git_diff() to use __git_complete_index_file() when '--' is
> present, or when revision reference completion yields no matching
> candidates, so that tracked paths are offered as candidates.
This changes behavior even in the case where '-C <there>' is not
used. The new behavior omits untracked paths from suggestions,
which is clearly better behavior.
I'll add the above paragraph to the proposed log message when I
queue this on 'seen'.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH] completion: complete tracked paths for 'git diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
2026-08-03 1:07 ` Junio C Hamano
@ 2026-08-03 5:44 ` SZEDER Gábor
2026-08-03 13:41 ` Junio C Hamano
2026-08-04 16:22 ` [PATCH v2] " Junio C Hamano
` (3 subsequent siblings)
5 siblings, 1 reply; 30+ messages in thread
From: SZEDER Gábor @ 2026-08-03 5:44 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt
On Sun, Aug 02, 2026 at 05:58:01PM -0700, Junio C Hamano wrote:
> When completing arguments for 'git diff', _git_diff() delegates to
> __git_complete_revlist_file(), which only completes revision
> references. This is good [*], as mixing both revs and paths in a
> single list to have the user pick is simply too confusing.
>
> If no reference matches, or if '--' is given, however, _git_diff()
> leaves COMPREPLY empty. Bash then falls back to default filename
> completion in $PWD. This fails when 'git -C <path>' is used because
> $PWD is not the target repository.
>
> Update _git_diff() to use __git_complete_index_file() when '--' is
> present, or when revision reference completion yields no matching
> candidates, so that tracked paths are offered as candidates.
Makes sense.
> [Footnote]
>
> * In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
> SZEDER made the same argument for "git send-email 0<TAB>".
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>
> * This is one of my pet peeves that I have raised a few times on
> the list but nobody reacted. So I did a "monkey see, monkey do"
> patch without deeply understanding what is going on in the code
> paths. When preparing the CC: list, I pulled a few folks, some
> very recognizable, some not recognizable immediately by me, out
> of "git shortlog --since=3.years" on this file.
Will have to finally polish and submit a completion patch from my
vaults to get myself back on this list ;)
> The contribution
> by any of them looked more expertly done by whatever I did here.
I think your changes are fine.
However, there is 'git diff --no-index' which happily accepts
untracked files as well, but with this patch the user can complete
only those untracked files that don't match the current word on the
command line (because then __git_complete_index_file() won't list
anything, and we'll fall back to Bash filename completion like
before).
I think we should check whether the '--no-index' option is present on
the command line, and simply not call __git_complete_index_file() if
it is, to let Bash list all paths; i.e. each of those calls should be
protected by an additional 'if test -z "$(__git_find_on_cmdline
"--no-index")' condition, perhaps.
> The use case is that I have a checkout of the 'todo' branch in an
> untracked subdirectory 'Meta' in my primary source tree. I would
> do
>
> $ git -C Meta status wh<TAB>
>
> and it completes to whats-cooking.txt just fine, 'add' also adds
> it, but 'diff' dies not work, not because I have refs that 'wh'
> completes, but because bash completion is unaware that I want
> paths completed in the other directory.
>
> contrib/completion/git-completion.bash | 8 +++++-
> t/t9902-completion.sh | 40 ++++++++++++++++++++++++++
> 2 files changed, 47 insertions(+), 1 deletion(-)
>
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index e875787710..8f5773292b 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1947,7 +1947,10 @@ __git_diff_difftool_options="--cached --staged
>
> _git_diff ()
> {
> - __git_has_doubledash && return
> + if __git_has_doubledash; then
> + __git_complete_index_file
> + return
> + fi
>
> case "$cur" in
> --diff-algorithm=*)
> @@ -1976,6 +1979,9 @@ _git_diff ()
> ;;
> esac
> __git_complete_revlist_file
> + if [ ${#COMPREPLY[@]} -eq 0 ]; then
> + __git_complete_index_file
> + fi
> }
>
> __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
> index 55dc9eabfc..eecd53f097 100755
> --- a/t/t9902-completion.sh
> +++ b/t/t9902-completion.sh
> @@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
> echo content >file1 &&
> echo more >file2 &&
> git add file1 file2 &&
> + echo untracked >file3 &&
> git commit -m one &&
> git branch mybranch &&
> git tag mytag
> @@ -2712,6 +2713,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
> EOF
> '
>
> +test_expect_success 'git diff completes tracked paths when no refs match' '
> + # file1 and file2 are tracked but file3 is not
> + test_completion "git diff f" <<-\EOF
> + file1
> + file2
> + EOF
> +'
> +
> +test_expect_success 'git diff -- completes tracked paths' '
> + # file1 and file2 are tracked but file3 is not
> + test_completion "git diff -- f" <<-\EOF
> + file1
> + file2
> + EOF
> +'
> +
> +test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
> + test_when_finished "rm -rf repo-for-diff" &&
> + git init repo-for-diff &&
> + echo content >repo-for-diff/otherfile &&
> + git -C repo-for-diff add otherfile &&
> + echo untracked >repo-for-diff/oops &&
> + git -C repo-for-diff commit -m otherfile &&
> + test_completion "git -C repo-for-diff diff o" <<-\EOF
> + otherfile
> + EOF
> +'
> +
> +test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo' '
> + test_when_finished "rm -rf repo-for-diff" &&
> + git init repo-for-diff &&
> + echo content >repo-for-diff/otherfile &&
> + git -C repo-for-diff add otherfile &&
> + git -C repo-for-diff commit -m otherfile &&
> + test_completion "git -C repo-for-diff diff -- o" <<-\EOF
> + otherfile
> + EOF
> +'
> +
> test_expect_success 'show completes all refs' '
> test_completion "git show m" <<-\EOF
> main Z
> --
> 2.55.0-607-g47e9082d35
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH] completion: complete tracked paths for 'git diff'
2026-08-03 5:44 ` SZEDER Gábor
@ 2026-08-03 13:41 ` Junio C Hamano
2026-08-03 15:45 ` Junio C Hamano
0 siblings, 1 reply; 30+ messages in thread
From: Junio C Hamano @ 2026-08-03 13:41 UTC (permalink / raw)
To: SZEDER Gábor
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt
SZEDER Gábor <szeder.dev@gmail.com> writes:
> I think we should check whether the '--no-index' option is present on
> the command line, and simply not call __git_complete_index_file() if
> it is, to let Bash list all paths; i.e. each of those calls should be
> protected by an additional 'if test -z "$(__git_find_on_cmdline
> "--no-index")' condition, perhaps.
Ah, I did not think of the "we made 'git diff' work without Git"
mode at all.
But I would avoid scanning the command line for '--no-index' for two
reasons:
(1) "git diff -S --no-index maint master" would not give you the
'--no-index' mode.
(2) When run outside the working tree of a repository, you do not
have to say '--no-index'.
These make detecting the "'git diff' but not Git" mode tedious and
error-prone.
I have not tried this, but if we arranged the code to fall back
further to Bash-native "paths in $PWD" after the completion code in
the posted patch found nothing, would it be sufficient?
When trying to complete an untracked file in the working tree of a
repository, if the file shares the same prefix with a tracked file,
you cannot complete the untracked file without a prefix long enough
to disambiguate it from the tracked one. Such a prefix may not even
exist (e.g., 'foo' is tracked and 'foo~' is untracked; when you type
"git diff --no-index foo<TAB>", you get 'foo' but not 'foo~', so you
end up typing 'foo~' in full). In that sense, it is a regression,
but supporting Git usage is the primary mission of 'git diff', so
the trade-off may not be so bad.
Thoughts?
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH] completion: complete tracked paths for 'git diff'
2026-08-03 13:41 ` Junio C Hamano
@ 2026-08-03 15:45 ` Junio C Hamano
0 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-03 15:45 UTC (permalink / raw)
To: SZEDER Gábor
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt
Junio C Hamano <gitster@pobox.com> writes:
> SZEDER Gábor <szeder.dev@gmail.com> writes:
>
>> I think we should check whether the '--no-index' option is present on
>> the command line, and simply not call __git_complete_index_file() if
>> it is, to let Bash list all paths; i.e. each of those calls should be
>> protected by an additional 'if test -z "$(__git_find_on_cmdline
>> "--no-index")' condition, perhaps.
>
>
> Ah, I did not think of the "we made 'git diff' work without Git"
> mode at all.
>
> But I would avoid scanning the command line for '--no-index' for two
> reasons:
>
> (1) "git diff -S --no-index maint master" would not give you the
> '--no-index' mode.
>
> (2) When run outside the working tree of a repository, you do not
> have to say '--no-index'.
>
> These make detecting the "'git diff' but not Git" mode tedious and
> error-prone.
>
> I have not tried this, but if we arranged the code to fall back
> further to Bash-native "paths in $PWD" after the completion code in
> the posted patch found nothing, would it be sufficient?
Eh, isn't the code already arranged to do so? With the posted
completion script loaded, I do
$ cd $HOME
$ git diff [--no-index] w/git.git/C<TAB>
where (1) my $HOME is not under version control (dotfiles are
installed after getting built from their sources that are version
controlled elsewhere), and (2) ~/w/git.git/ is the primary working
tree I work in. I see
Cargo.toml CODE_OF_CONDUCT.md COPYING
offered as choices. As there is no index or rev when I am in my
$HOME directory, naturally the only completion that kicks in is the
bash native "paths we see".
So I think I do not need anything special to "arrange" the fallback.
Thanks.
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v2] completion: complete tracked paths for 'git diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
2026-08-03 1:07 ` Junio C Hamano
2026-08-03 5:44 ` SZEDER Gábor
@ 2026-08-04 16:22 ` Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
` (2 subsequent siblings)
5 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-04 16:22 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt
When completing arguments for 'git diff', _git_diff() delegates to
__git_complete_revlist_file(), which only completes revision
references. This is good [*], as mixing both revisions and paths in a
single list for the user to pick from is simply too confusing.
If no reference matches, or if '--' is given, however, _git_diff()
leaves COMPREPLY empty. Bash then falls back to default filename
completion in $PWD. This fails when 'git -C <path>' is used because
$PWD is not the target repository.
Update _git_diff() to use __git_complete_index_file() when '--' is
present, or when revision reference completion yields no matching
candidates, so that tracked paths are offered as candidates.
This changes behavior even in the case where '-C <there>' is not
used. The new behavior omits untracked paths from suggestions when
no revs match the prefix but matching tracked paths exist, which is
more useful in the context of 'git diff'.
When run outside the working tree of a repository, or when nothing
matches from revisions or tracked paths, Bash still falls back to
default filename completion in $PWD, so such a use case would be
just like completing paths for any 'diff' command, rather than for
'git diff'.
[Footnote]
* In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
SZEDER made the same argument for "git send-email 0<TAB>".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
* The last two paragraphs in the proposed commit log message are
new, to explain why the code posted as-is would be sufficient to
support the "'git diff --no-index' is not Git but is diff" usage,
and there is no code change between v1 and this iteration.
By the way, I, as a relative newbie to the completion script, had
trouble with the test_completion helper and wasted some time
wondering why an additional test:
test_expect_success 'git diff completes untracked paths if nothing matches' '
>untracked &&
test_completion "git diff -- u" <<-\EOF
untracked
EOF
'
did not work, even though under manual testing, u<TAB> completed
'untracked' just fine. The reason is that test_completion
does not test the final "Bash default" fallback. It might not
be necessary for those who are familiar with the completion test
suite, but I thought it would help others.
This message comes with a range-diff that shows only the commit
log changes.
contrib/completion/git-completion.bash | 8 +++++-
t/t9902-completion.sh | 40 ++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e875787710..8f5773292b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1947,7 +1947,10 @@ __git_diff_difftool_options="--cached --staged
_git_diff ()
{
- __git_has_doubledash && return
+ if __git_has_doubledash; then
+ __git_complete_index_file
+ return
+ fi
case "$cur" in
--diff-algorithm=*)
@@ -1976,6 +1979,9 @@ _git_diff ()
;;
esac
__git_complete_revlist_file
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 9ae3c48ebd..82488f3b50 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
echo content >file1 &&
echo more >file2 &&
git add file1 file2 &&
+ echo untracked >file3 &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -2712,6 +2713,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
EOF
'
+test_expect_success 'git diff completes tracked paths when no refs match' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git diff -- completes tracked paths' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff -- f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ echo untracked >repo-for-diff/oops &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff o" <<-\EOF
+ otherfile
+ EOF
+'
+
+test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ otherfile
+ EOF
+'
+
test_expect_success 'show completes all refs' '
test_completion "git show m" <<-\EOF
main Z
Range-diff against v1:
1: 100043822f ! 1: fa4461a192 completion: complete tracked paths for 'git diff'
@@ Commit message
When completing arguments for 'git diff', _git_diff() delegates to
__git_complete_revlist_file(), which only completes revision
- references. This is good [*], as mixing both revs and paths in a
- single list to have the user pick is simply too confusing.
+ references. This is good [*], as mixing both revisions and paths in a
+ single list for the user to pick from is simply too confusing.
If no reference matches, or if '--' is given, however, _git_diff()
- leaves COMPREPLY empty. Bash then falls back to default filename
- completion in $PWD. This fails when 'git -C <path>' is used because
+ leaves COMPREPLY empty. Bash then falls back to default filename
+ completion in $PWD. This fails when 'git -C <path>' is used because
$PWD is not the target repository.
Update _git_diff() to use __git_complete_index_file() when '--' is
present, or when revision reference completion yields no matching
candidates, so that tracked paths are offered as candidates.
- [Footnote]
+ This changes behavior even in the case where '-C <there>' is not
+ used. The new behavior omits untracked paths from suggestions when
+ no revs match the prefix but matching tracked paths exist, which is
+ more useful in the context of 'git diff'.
+
+ When run outside the working tree of a repository, or when nothing
+ matches from revisions or tracked paths, Bash still falls back to
+ default filename completion in $PWD, so such a use case would be
+ just like completing paths for any 'diff' command, rather than for
+ 'git diff'.
+ [Footnote]
* In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
SZEDER made the same argument for "git send-email 0<TAB>".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
+ ---
+
+ * The last two paragraphs in the proposed commit log message are
+ new, to explain why the code posted as-is would be sufficient to
+ support the "'git diff --no-index' is not Git but is diff" usage.
+ I, as a relative newbie to the completion script, had trouble
+ with the test_completion helper and wasted some time wondering
+ why an additional test:
+
+ test_expect_success 'git diff completes untracked paths if nothing matches' '
+ >untracked &&
+ test_completion "git diff -- u" <<-\EOF
+ untracked
+ EOF
+ '
+
+ did not work, even though under manual testing, u<TAB> completed
+ 'untracked' just fine. The reason is that test_completion
+ does not test the final "Bash default" fallback. It might not
+ be necessary for those who are familiar with the completion test
+ suite, but I thought it would help others.
## contrib/completion/git-completion.bash ##
@@ contrib/completion/git-completion.bash: __git_diff_difftool_options="--cached --staged
--
2.55.0-624-gcdeb5fd34c
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v3 0/3] completion of 'git [-C <dir>] diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
` (2 preceding siblings ...)
2026-08-04 16:22 ` [PATCH v2] " Junio C Hamano
@ 2026-08-05 19:42 ` Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 1/3] completion: no-op refactoring of diff completion Junio C Hamano
` (3 more replies)
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
5 siblings, 4 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-05 19:42 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
Here is another reroll.
The primary motivation for this topic is that the command-line
completion of 'git diff' does not handle paths (unlike 'git status'
and 'git add') and instead relies on the default behavior of Bash
command-line completion, which completes files in $PWD; this does
not work at all with the '-C <directory>' option.
Previous iterations of the patch taught the completion script to
offer tracked paths that match the prefix before <TAB> to improve
the situation.
This time, we also complete untracked paths ourselves, so that even
the following commands, which compare files like 'file[12]' that are
not under the control of Git in a different directory, are
completed:
$ git -C not-a-git-dir diff fil<TAB>
$ git -C not-a-git-dir diff --no-index fil<TAB>
1/3: completion: no-op refactoring of diff completion
2/3: completion: complete tracked paths for 'git diff'
3/3: completion: 'git diff' completes untracked paths as a last resort
contrib/completion/git-completion.bash | 69 +++++++++++++++-----------
t/t9902-completion.sh | 59 ++++++++++++++++++++++
2 files changed, 100 insertions(+), 28 deletions(-)
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH v3 1/3] completion: no-op refactoring of diff completion
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
@ 2026-08-05 19:42 ` Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
` (2 subsequent siblings)
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-05 19:42 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
The "git diff" completion function punts very early when it sees
"--" on the command line, since it is a sign that options or
revisions can appear and the current completion does not need to do
anything "git diff" specific. By returning, it lets Bash default
action that completes the names of the files in $PWD to kick in.
In preparation for the next step to change what happens when we
"punt", arrange the code flow to avoid this early return. The
behaviour at this step is unchanged, but the control flow just
falls straight to the end.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 61 ++++++++++++++------------
1 file changed, 33 insertions(+), 28 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e875787710..ccd3b2a372 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1947,35 +1947,40 @@ __git_diff_difftool_options="--cached --staged
_git_diff ()
{
- __git_has_doubledash && return
-
- case "$cur" in
- --diff-algorithm=*)
- __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
- return
- ;;
- --submodule=*)
- __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
- return
- ;;
- --color-moved=*)
- __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
- return
- ;;
- --color-moved-ws=*)
- __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
- return
- ;;
- --ws-error-highlight=*)
- __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
- return
- ;;
- --*)
- __gitcomp "$__git_diff_difftool_options"
- return
+ if ! __git_has_doubledash; then
+ case "$cur" in
+ --diff-algorithm=*)
+ __gitcomp "$__git_diff_algorithms" \
+ "" "${cur##--diff-algorithm=}"
+ return
;;
- esac
- __git_complete_revlist_file
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" \
+ "" "${cur##--submodule=}"
+ return
+ ;;
+ --color-moved=*)
+ __gitcomp "$__git_color_moved_opts" \
+ "" "${cur##--color-moved=}"
+ return
+ ;;
+ --color-moved-ws=*)
+ __gitcomp "$__git_color_moved_ws_opts" \
+ "" "${cur##--color-moved-ws=}"
+ return
+ ;;
+ --ws-error-highlight=*)
+ __gitcomp "$__git_ws_error_highlight_opts" \
+ "" "${cur##--ws-error-highlight=}"
+ return
+ ;;
+ --*)
+ __gitcomp "$__git_diff_difftool_options"
+ return
+ ;;
+ esac
+ __git_complete_revlist_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
--
2.55.0-653-g9745b9777e
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v3 2/3] completion: complete tracked paths for 'git diff'
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 1/3] completion: no-op refactoring of diff completion Junio C Hamano
@ 2026-08-05 19:42 ` Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
2026-08-06 11:30 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' D. Ben Knoble
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-05 19:42 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
When completing arguments for 'git diff', _git_diff() delegates to
__git_complete_revlist_file(), which only completes revision
references. This is good [*], as mixing both revisions and paths in a
single list for the user to pick from is simply too confusing.
If no reference matches, or if '--' is given, however, _git_diff()
leaves COMPREPLY empty. Bash then falls back to default filename
completion in $PWD. This fails when 'git -C <path>' is used because
$PWD is not the target repository.
Update _git_diff() to use __git_complete_index_file() when '--' is
present, or when revision reference completion yields no matching
candidates, so that tracked paths are offered as candidates.
This changes behavior even in the case where '-C <there>' is not
used. The new behavior omits untracked paths from suggestions when
no revs match the prefix but matching tracked paths exist, which is
more useful in the context of 'git diff'.
When run outside the working tree of a repository, or when nothing
matches from revisions or tracked paths, Bash still falls back to
default filename completion in $PWD, so such a use case would be
just like completing paths for any 'diff' command, rather than for
'git diff'.
[Footnote]
* In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
SZEDER made the same argument for "git send-email 0<TAB>".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 +++
t/t9902-completion.sh | 39 ++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index ccd3b2a372..845fd19f70 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1981,6 +1981,10 @@ _git_diff ()
esac
__git_complete_revlist_file
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 55dc9eabfc..10ac690e21 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2712,6 +2712,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
EOF
'
+test_expect_success 'git diff completes tracked paths when no refs match' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git diff -- completes tracked paths' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff -- f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ echo untracked >repo-for-diff/oops &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff o" <<-\EOF
+ otherfile
+ EOF
+'
+
+test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ otherfile
+ EOF
+'
+
test_expect_success 'show completes all refs' '
test_completion "git show m" <<-\EOF
main Z
--
2.55.0-653-g9745b9777e
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 1/3] completion: no-op refactoring of diff completion Junio C Hamano
2026-08-05 19:42 ` [PATCH v3 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
@ 2026-08-05 19:42 ` Junio C Hamano
2026-08-06 11:30 ` D. Ben Knoble
2026-08-06 11:30 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' D. Ben Knoble
3 siblings, 1 reply; 30+ messages in thread
From: Junio C Hamano @ 2026-08-05 19:42 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
We taught 'git diff' to first try to complete revisions (unless '--'
is present on the command line) and, failing that, to complete
tracked paths. If this yields nothing, it lets the Bash default,
which offers paths in $PWD, kick in.
Teach it to complete untracked paths before giving up and letting
the Bash default kick in. With this change,
$ git -C another-directory diff un<TAB>
finds the 'untracked' file in another-directory and offers it as a
completion candidate.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 ++++
t/t9902-completion.sh | 22 +++++++++++++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 845fd19f70..7741789e41 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1985,6 +1985,10 @@ _git_diff ()
if [ ${#COMPREPLY[@]} -eq 0 ]; then
__git_complete_index_file
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file "--others --directory"
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 10ac690e21..53a2bfb2ac 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
echo content >file1 &&
echo more >file2 &&
git add file1 file2 &&
+ echo untracked >ufile &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -2728,6 +2729,15 @@ test_expect_success 'git diff -- completes tracked paths' '
EOF
'
+test_expect_success 'git diff [--] completes untracked paths, too' '
+ test_completion "git diff u" <<-\EOF &&
+ ufile
+ EOF
+ test_completion "git diff -- u" <<-\EOF
+ ufile
+ EOF
+'
+
test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
@@ -2744,11 +2754,21 @@ test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
echo content >repo-for-diff/otherfile &&
+ echo untracked >repo-for-diff/untracked &&
git -C repo-for-diff add otherfile &&
git -C repo-for-diff commit -m otherfile &&
- test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
+ otherfile
+ EOF
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF &&
otherfile
EOF
+ test_completion "git -C repo-for-diff diff u" <<-\EOF &&
+ untracked
+ EOF
+ test_completion "git -C repo-for-diff diff -- u" <<-\EOF
+ untracked
+ EOF
'
test_expect_success 'show completes all refs' '
--
2.55.0-653-g9745b9777e
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort
2026-08-05 19:42 ` [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
@ 2026-08-06 11:30 ` D. Ben Knoble
2026-08-06 15:06 ` Junio C Hamano
0 siblings, 1 reply; 30+ messages in thread
From: D. Ben Knoble @ 2026-08-06 11:30 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
Hello Junio,
On Wed, Aug 5, 2026 at 3:45 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> We taught 'git diff' to first try to complete revisions (unless '--'
> is present on the command line) and, failing that, to complete
> tracked paths. If this yields nothing, it lets the Bash default,
> which offers paths in $PWD, kick in.
>
> Teach it to complete untracked paths before giving up and letting
> the Bash default kick in. With this change,
>
> $ git -C another-directory diff un<TAB>
>
> finds the 'untracked' file in another-directory and offers it as a
> completion candidate.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> contrib/completion/git-completion.bash | 4 ++++
> t/t9902-completion.sh | 22 +++++++++++++++++++++-
> 2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index 845fd19f70..7741789e41 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1985,6 +1985,10 @@ _git_diff ()
> if [ ${#COMPREPLY[@]} -eq 0 ]; then
> __git_complete_index_file
> fi
> +
> + if [ ${#COMPREPLY[@]} -eq 0 ]; then
> + __git_complete_index_file "--others --directory"
> + fi
> }
>
> __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
> index 10ac690e21..53a2bfb2ac 100755
> --- a/t/t9902-completion.sh
> +++ b/t/t9902-completion.sh
> @@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
> echo content >file1 &&
> echo more >file2 &&
> git add file1 file2 &&
> + echo untracked >ufile &&
> git commit -m one &&
> git branch mybranch &&
> git tag mytag
> @@ -2728,6 +2729,15 @@ test_expect_success 'git diff -- completes tracked paths' '
> EOF
> '
>
> +test_expect_success 'git diff [--] completes untracked paths, too' '
> + test_completion "git diff u" <<-\EOF &&
> + ufile
> + EOF
> + test_completion "git diff -- u" <<-\EOF
> + ufile
> + EOF
> +'
> +
LGTM up to here.
> test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
> test_when_finished "rm -rf repo-for-diff" &&
> git init repo-for-diff &&
> @@ -2744,11 +2754,21 @@ test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo
> test_when_finished "rm -rf repo-for-diff" &&
> git init repo-for-diff &&
> echo content >repo-for-diff/otherfile &&
> + echo untracked >repo-for-diff/untracked &&
> git -C repo-for-diff add otherfile &&
> git -C repo-for-diff commit -m otherfile &&
> - test_completion "git -C repo-for-diff diff -- o" <<-\EOF
> + test_completion "git -C repo-for-diff diff o" <<-\EOF &&
> + otherfile
> + EOF
Here, with more context (which I won't paste, because GMail…), it
looks like this test is redundant with the test just before?
> + test_completion "git -C repo-for-diff diff -- o" <<-\EOF &&
> otherfile
> EOF
> + test_completion "git -C repo-for-diff diff u" <<-\EOF &&
> + untracked
> + EOF
> + test_completion "git -C repo-for-diff diff -- u" <<-\EOF
> + untracked
> + EOF
> '
These tests intermingle with -- and without; the other tests separated them.
I don't think I have a strong preference, but perhaps consistency is a
good ideal?
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort
2026-08-06 11:30 ` D. Ben Knoble
@ 2026-08-06 15:06 ` Junio C Hamano
0 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-06 15:06 UTC (permalink / raw)
To: D. Ben Knoble
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
"D. Ben Knoble" <ben.knoble@gmail.com> writes:
>> test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
>> test_when_finished "rm -rf repo-for-diff" &&
>> git init repo-for-diff &&
>> @@ -2744,11 +2754,21 @@ test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo
>> test_when_finished "rm -rf repo-for-diff" &&
>> git init repo-for-diff &&
>> echo content >repo-for-diff/otherfile &&
>> + echo untracked >repo-for-diff/untracked &&
>> git -C repo-for-diff add otherfile &&
>> git -C repo-for-diff commit -m otherfile &&
>> - test_completion "git -C repo-for-diff diff -- o" <<-\EOF
>> + test_completion "git -C repo-for-diff diff o" <<-\EOF &&
>> + otherfile
>> + EOF
>
> Here, with more context (which I won't paste, because GMail…), it
> looks like this test is redundant with the test just before?
You're right. As these prefix strings ('f', 'o', or 'u') tested
do not begin any valid refs, the result should be the same with or
without '--', and I wanted to test both in a single test block, but
I botched the conversion.
Will fix in v4, which hopefully will be test-only updates.
Thanks.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 0/3] completion of 'git [-C <dir>] diff'
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
` (2 preceding siblings ...)
2026-08-05 19:42 ` [PATCH v3 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
@ 2026-08-06 11:30 ` D. Ben Knoble
3 siblings, 0 replies; 30+ messages in thread
From: D. Ben Knoble @ 2026-08-06 11:30 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
Hi Junio,
On Wed, Aug 5, 2026 at 3:44 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Here is another reroll.
>
> The primary motivation for this topic is that the command-line
> completion of 'git diff' does not handle paths (unlike 'git status'
> and 'git add') and instead relies on the default behavior of Bash
> command-line completion, which completes files in $PWD; this does
> not work at all with the '-C <directory>' option.
>
> Previous iterations of the patch taught the completion script to
> offer tracked paths that match the prefix before <TAB> to improve
> the situation.
>
> This time, we also complete untracked paths ourselves, so that even
> the following commands, which compare files like 'file[12]' that are
> not under the control of Git in a different directory, are
> completed:
>
> $ git -C not-a-git-dir diff fil<TAB>
> $ git -C not-a-git-dir diff --no-index fil<TAB>
>
> 1/3: completion: no-op refactoring of diff completion
> 2/3: completion: complete tracked paths for 'git diff'
> 3/3: completion: 'git diff' completes untracked paths as a last resort
>
> contrib/completion/git-completion.bash | 69 +++++++++++++++-----------
> t/t9902-completion.sh | 59 ++++++++++++++++++++++
> 2 files changed, 100 insertions(+), 28 deletions(-)
>
Left one comment on 3/3, but the rest looks good!
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v4 0/3] completion of 'git [-C <dir>] diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
` (3 preceding siblings ...)
2026-08-05 19:42 ` [PATCH v3 0/3] completion of 'git [-C <dir>] diff' Junio C Hamano
@ 2026-08-07 1:38 ` Junio C Hamano
2026-08-07 1:38 ` [PATCH v4 1/3] completion: no-op refactoring of diff completion Junio C Hamano
` (3 more replies)
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
5 siblings, 4 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 1:38 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
The primary motivation for this topic is that the command-line
completion of 'git diff' does not handle paths (unlike 'git status'
and 'git add') and instead relies on the default behavior of Bash
command-line completion, which completes files in $PWD; this does
not work at all with the '-C <directory>' option.
This series teaches the completion machinery to complete revisions
(unless '--' exists), then tracked paths, and then untracked paths,
before letting the Bash default kick in. This way, we correctly
complete 'git diff' command line even when '-C <directory>' is in
effect.
The tests are the only changes relative to v2. In the step where
tracked paths are completed, v2 did not demonstrate that untracked
ones are *not* completed at the same time. Now we do by having
untracked 'file3' next to 'file1' and 'file2' that are tracked. In
the last step, we demonstrate untracked paths that do not share
prefix with refs or tracked paths are completed, with or without the
"-C <dir>" option.
1/3: completion: no-op refactoring of diff completion
2/3: completion: complete tracked paths for 'git diff'
3/3: completion: 'git diff' completes untracked paths as a last
resort
contrib/completion/git-completion.bash | 69 +++++++++++++++-----------
t/t9902-completion.sh | 49 ++++++++++++++++++
2 files changed, 90 insertions(+), 28 deletions(-)
Range-diff against v3:
1: d3c51c042c = 1: 3b99b45fee completion: no-op refactoring of diff completion
2: c3658d6ca2 ! 2: bcc24b6bda completion: complete tracked paths for 'git diff'
@@ contrib/completion/git-completion.bash: _git_diff ()
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
## t/t9902-completion.sh ##
+@@ t/t9902-completion.sh: test_expect_success 'setup for integration tests' '
+ echo content >file1 &&
+ echo more >file2 &&
+ git add file1 file2 &&
++ echo untracked >file3 &&
+ git commit -m one &&
+ git branch mybranch &&
+ git tag mytag
@@ t/t9902-completion.sh: test_expect_success 'git -C <path> checkout uses the right repo' '
EOF
'
+test_expect_success 'git diff completes tracked paths when no refs match' '
+ # file1 and file2 are tracked but file3 is not
-+ test_completion "git diff f" <<-\EOF
++ # there is no ref that begins with f
++ test_completion "git diff f" <<-\EOF &&
+ file1
+ file2
+ EOF
-+'
-+
-+test_expect_success 'git diff -- completes tracked paths' '
-+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff -- f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
-+test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
++test_expect_success 'git -C <path> diff completes in the specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
++
++ # otherfile is tracked, oops is untracked
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
-+ echo untracked >repo-for-diff/oops &&
+ git -C repo-for-diff commit -m otherfile &&
-+ test_completion "git -C repo-for-diff diff o" <<-\EOF
++ echo untracked >repo-for-diff/oops &&
++ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
+ otherfile
+ EOF
-+'
-+
-+test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo' '
-+ test_when_finished "rm -rf repo-for-diff" &&
-+ git init repo-for-diff &&
-+ echo content >repo-for-diff/otherfile &&
-+ git -C repo-for-diff add otherfile &&
-+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ otherfile
+ EOF
3: ba5dc6f164 ! 3: 34720a30ab completion: 'git diff' completes untracked paths as a last resort
@@ contrib/completion/git-completion.bash: _git_diff ()
## t/t9902-completion.sh ##
@@ t/t9902-completion.sh: test_expect_success 'setup for integration tests' '
- echo content >file1 &&
echo more >file2 &&
git add file1 file2 &&
+ echo untracked >file3 &&
+ echo untracked >ufile &&
git commit -m one &&
git branch mybranch &&
git tag mytag
-@@ t/t9902-completion.sh: test_expect_success 'git diff -- completes tracked paths' '
+@@ t/t9902-completion.sh: test_expect_success 'git diff completes tracked paths when no refs match' '
EOF
'
+test_expect_success 'git diff [--] completes untracked paths, too' '
++ # there is no ref or tracked path that begin with u
+ test_completion "git diff u" <<-\EOF &&
+ ufile
+ EOF
@@ t/t9902-completion.sh: test_expect_success 'git diff -- completes tracked paths'
+ EOF
+'
+
- test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
- test_when_finished "rm -rf repo-for-diff" &&
- git init repo-for-diff &&
-@@ t/t9902-completion.sh: test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo
+ test_expect_success 'git -C <path> diff completes in the specified repo' '
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
+
+- # otherfile is tracked, oops is untracked
++ # otherfile is tracked, oops and ufile are untracked
echo content >repo-for-diff/otherfile &&
-+ echo untracked >repo-for-diff/untracked &&
git -C repo-for-diff add otherfile &&
git -C repo-for-diff commit -m otherfile &&
+ echo untracked >repo-for-diff/oops &&
++ echo untracked >repo-for-diff/ufile &&
+ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
+ otherfile
+ EOF
- test_completion "git -C repo-for-diff diff -- o" <<-\EOF
-+ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
-+ otherfile
-+ EOF
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF &&
otherfile
EOF
+ test_completion "git -C repo-for-diff diff u" <<-\EOF &&
-+ untracked
++ ufile
+ EOF
+ test_completion "git -C repo-for-diff diff -- u" <<-\EOF
-+ untracked
++ ufile
+ EOF
'
--
2.55.0-655-gb2c071042d
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH v4 1/3] completion: no-op refactoring of diff completion
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
@ 2026-08-07 1:38 ` Junio C Hamano
2026-08-07 6:15 ` Elijah Newren
2026-08-07 1:38 ` [PATCH v4 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
` (2 subsequent siblings)
3 siblings, 1 reply; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 1:38 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
The "git diff" completion function punts very early when it sees
"--" on the command line, since it is a sign that options or
revisions can appear and the current completion does not need to do
anything "git diff" specific. By returning, it lets Bash default
action that completes the names of the files in $PWD to kick in.
In preparation for the next step to change what happens when we
"punt", arrange the code flow to avoid this early return. The
behaviour at this step is unchanged, but the control flow just
falls straight to the end.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 61 ++++++++++++++------------
1 file changed, 33 insertions(+), 28 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e875787710..ccd3b2a372 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1947,35 +1947,40 @@ __git_diff_difftool_options="--cached --staged
_git_diff ()
{
- __git_has_doubledash && return
-
- case "$cur" in
- --diff-algorithm=*)
- __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
- return
- ;;
- --submodule=*)
- __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
- return
- ;;
- --color-moved=*)
- __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
- return
- ;;
- --color-moved-ws=*)
- __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
- return
- ;;
- --ws-error-highlight=*)
- __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
- return
- ;;
- --*)
- __gitcomp "$__git_diff_difftool_options"
- return
+ if ! __git_has_doubledash; then
+ case "$cur" in
+ --diff-algorithm=*)
+ __gitcomp "$__git_diff_algorithms" \
+ "" "${cur##--diff-algorithm=}"
+ return
;;
- esac
- __git_complete_revlist_file
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" \
+ "" "${cur##--submodule=}"
+ return
+ ;;
+ --color-moved=*)
+ __gitcomp "$__git_color_moved_opts" \
+ "" "${cur##--color-moved=}"
+ return
+ ;;
+ --color-moved-ws=*)
+ __gitcomp "$__git_color_moved_ws_opts" \
+ "" "${cur##--color-moved-ws=}"
+ return
+ ;;
+ --ws-error-highlight=*)
+ __gitcomp "$__git_ws_error_highlight_opts" \
+ "" "${cur##--ws-error-highlight=}"
+ return
+ ;;
+ --*)
+ __gitcomp "$__git_diff_difftool_options"
+ return
+ ;;
+ esac
+ __git_complete_revlist_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
--
2.55.0-655-gb2c071042d
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v4 1/3] completion: no-op refactoring of diff completion
2026-08-07 1:38 ` [PATCH v4 1/3] completion: no-op refactoring of diff completion Junio C Hamano
@ 2026-08-07 6:15 ` Elijah Newren
2026-08-07 15:09 ` Junio C Hamano
0 siblings, 1 reply; 30+ messages in thread
From: Elijah Newren @ 2026-08-07 6:15 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> The "git diff" completion function punts very early when it sees
> "--" on the command line, since it is a sign that options or
> revisions can appear and the current completion does not need to do
> anything "git diff" specific. By returning, it lets Bash default
> action that completes the names of the files in $PWD to kick in.
>
> In preparation for the next step to change what happens when we
> "punt", arrange the code flow to avoid this early return. The
> behaviour at this step is unchanged, but the control flow just
> falls straight to the end.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> contrib/completion/git-completion.bash | 61 ++++++++++++++------------
> 1 file changed, 33 insertions(+), 28 deletions(-)
>
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index e875787710..ccd3b2a372 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1947,35 +1947,40 @@ __git_diff_difftool_options="--cached --staged
>
> _git_diff ()
> {
[...]
> + if ! __git_has_doubledash; then
> + case "$cur" in
> + --diff-algorithm=*)
> + __gitcomp "$__git_diff_algorithms" \
> + "" "${cur##--diff-algorithm=}"
> + return
> ;;
The refactor in this commit is a faithful no-op -- every arm got
re-indented by one tab as expected. One tiny slip, though: this first
case's ";;" didn't get the extra tab that every other arm received.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v4 1/3] completion: no-op refactoring of diff completion
2026-08-07 6:15 ` Elijah Newren
@ 2026-08-07 15:09 ` Junio C Hamano
0 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 15:09 UTC (permalink / raw)
To: Elijah Newren
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
Elijah Newren <newren@gmail.com> writes:
> On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> The "git diff" completion function punts very early when it sees
>> "--" on the command line, since it is a sign that options or
>> revisions can appear and the current completion does not need to do
>> anything "git diff" specific. By returning, it lets Bash default
>> action that completes the names of the files in $PWD to kick in.
>>
>> In preparation for the next step to change what happens when we
>> "punt", arrange the code flow to avoid this early return. The
>> behaviour at this step is unchanged, but the control flow just
>> falls straight to the end.
>>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> ---
>> contrib/completion/git-completion.bash | 61 ++++++++++++++------------
>> 1 file changed, 33 insertions(+), 28 deletions(-)
>>
>> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
>> index e875787710..ccd3b2a372 100644
>> --- a/contrib/completion/git-completion.bash
>> +++ b/contrib/completion/git-completion.bash
>> @@ -1947,35 +1947,40 @@ __git_diff_difftool_options="--cached --staged
>>
>> _git_diff ()
>> {
> [...]
>> + if ! __git_has_doubledash; then
>> + case "$cur" in
>> + --diff-algorithm=*)
>> + __gitcomp "$__git_diff_algorithms" \
>> + "" "${cur##--diff-algorithm=}"
>> + return
>> ;;
>
> The refactor in this commit is a faithful no-op -- every arm got
> re-indented by one tab as expected. One tiny slip, though: this first
> case's ";;" didn't get the extra tab that every other arm received.
Good eyes. Will fix.
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v4 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
2026-08-07 1:38 ` [PATCH v4 1/3] completion: no-op refactoring of diff completion Junio C Hamano
@ 2026-08-07 1:38 ` Junio C Hamano
2026-08-07 6:18 ` Elijah Newren
2026-08-07 1:38 ` [PATCH v4 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
2026-08-07 6:31 ` [PATCH v4 0/3] completion of 'git [-C <dir>] diff' Elijah Newren
3 siblings, 1 reply; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 1:38 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
When completing arguments for 'git diff', _git_diff() delegates to
__git_complete_revlist_file(), which only completes revision
references. This is good [*], as mixing both revisions and paths in a
single list for the user to pick from is simply too confusing.
If no reference matches, or if '--' is given, however, _git_diff()
leaves COMPREPLY empty. Bash then falls back to default filename
completion in $PWD. This fails when 'git -C <path>' is used because
$PWD is not the target repository.
Update _git_diff() to use __git_complete_index_file() when '--' is
present, or when revision reference completion yields no matching
candidates, so that tracked paths are offered as candidates.
This changes behavior even in the case where '-C <there>' is not
used. The new behavior omits untracked paths from suggestions when
no revs match the prefix but matching tracked paths exist, which is
more useful in the context of 'git diff'.
When run outside the working tree of a repository, or when nothing
matches from revisions or tracked paths, Bash still falls back to
default filename completion in $PWD, so such a use case would be
just like completing paths for any 'diff' command, rather than for
'git diff'.
[Footnote]
* In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
SZEDER made the same argument for "git send-email 0<TAB>".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 ++++
t/t9902-completion.sh | 31 ++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index ccd3b2a372..845fd19f70 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1981,6 +1981,10 @@ _git_diff ()
esac
__git_complete_revlist_file
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 55dc9eabfc..adfaf414fd 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
echo content >file1 &&
echo more >file2 &&
git add file1 file2 &&
+ echo untracked >file3 &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -2712,6 +2713,36 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
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
+ test_completion "git diff f" <<-\EOF &&
+ file1
+ file2
+ EOF
+ test_completion "git diff -- f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git -C <path> diff completes in the specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+
+ # otherfile is tracked, oops is untracked
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ git -C repo-for-diff commit -m otherfile &&
+ echo untracked >repo-for-diff/oops &&
+ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
+ otherfile
+ EOF
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ otherfile
+ EOF
+'
+
test_expect_success 'show completes all refs' '
test_completion "git show m" <<-\EOF
main Z
--
2.55.0-655-gb2c071042d
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v4 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 1:38 ` [PATCH v4 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
@ 2026-08-07 6:18 ` Elijah Newren
2026-08-07 11:02 ` D. Ben Knoble
2026-08-07 15:13 ` Junio C Hamano
0 siblings, 2 replies; 30+ messages in thread
From: Elijah Newren @ 2026-08-07 6:18 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> When completing arguments for 'git diff', _git_diff() delegates to
> __git_complete_revlist_file(), which only completes revision
> references. This is good [*], as mixing both revisions and paths in a
> single list for the user to pick from is simply too confusing.
>
> If no reference matches, or if '--' is given, however, _git_diff()
> leaves COMPREPLY empty. Bash then falls back to default filename
> completion in $PWD. This fails when 'git -C <path>' is used because
> $PWD is not the target repository.
>
> Update _git_diff() to use __git_complete_index_file() when '--' is
> present, or when revision reference completion yields no matching
> candidates, so that tracked paths are offered as candidates.
>
> This changes behavior even in the case where '-C <there>' is not
> used. The new behavior omits untracked paths from suggestions when
> no revs match the prefix but matching tracked paths exist, which is
> more useful in the context of 'git diff'.
I'm looking forward to using this. :-)
[...]
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index ccd3b2a372..845fd19f70 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1981,6 +1981,10 @@ _git_diff ()
> esac
> __git_complete_revlist_file
> fi
> +
> + if [ ${#COMPREPLY[@]} -eq 0 ]; then
> + __git_complete_index_file
> + fi
> }
Curious; __git_complete_index_file() is documented as "requires 1
argument", but you pass none here. As far as I can tell, it works
anyway, but feels like an accident:
1. __git_complete_index_file CALLS
__git_index_files "$1" ...
(Here, "$1" == "")
2. __git_index_files "$1" ... CALLS
__git_ls_files_helper "$root" "$1" ...
(Here, "$1" == "", again)
3. __git_ls_files_helper "$root" "$1" CALLS
__git -C "$1" -c core.quotePath=false ls-files
--exclude-standard $2 -- ...
(Note that $2 is unquoted, and since it's empty, it disappears)
It seems like it'd be better to pass an explicit "" to
__git_complete_index_file than to implicitly get it.
[...]
The rest looks good.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v4 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 6:18 ` Elijah Newren
@ 2026-08-07 11:02 ` D. Ben Knoble
2026-08-07 15:13 ` Junio C Hamano
1 sibling, 0 replies; 30+ messages in thread
From: D. Ben Knoble @ 2026-08-07 11:02 UTC (permalink / raw)
To: Elijah Newren
Cc: Junio C Hamano, git, Philippe Blain, Britton Leo Kerin,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
On Fri, Aug 7, 2026 at 2:18 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > When completing arguments for 'git diff', _git_diff() delegates to
> > __git_complete_revlist_file(), which only completes revision
> > references. This is good [*], as mixing both revisions and paths in a
> > single list for the user to pick from is simply too confusing.
> >
> > If no reference matches, or if '--' is given, however, _git_diff()
> > leaves COMPREPLY empty. Bash then falls back to default filename
> > completion in $PWD. This fails when 'git -C <path>' is used because
> > $PWD is not the target repository.
> >
> > Update _git_diff() to use __git_complete_index_file() when '--' is
> > present, or when revision reference completion yields no matching
> > candidates, so that tracked paths are offered as candidates.
> >
> > This changes behavior even in the case where '-C <there>' is not
> > used. The new behavior omits untracked paths from suggestions when
> > no revs match the prefix but matching tracked paths exist, which is
> > more useful in the context of 'git diff'.
>
> I'm looking forward to using this. :-)
>
> [...]
> > diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> > index ccd3b2a372..845fd19f70 100644
> > --- a/contrib/completion/git-completion.bash
> > +++ b/contrib/completion/git-completion.bash
> > @@ -1981,6 +1981,10 @@ _git_diff ()
> > esac
> > __git_complete_revlist_file
> > fi
> > +
> > + if [ ${#COMPREPLY[@]} -eq 0 ]; then
> > + __git_complete_index_file
> > + fi
> > }
>
> Curious; __git_complete_index_file() is documented as "requires 1
> argument", but you pass none here. As far as I can tell, it works
> anyway, but feels like an accident:
>
> 1. __git_complete_index_file CALLS
> __git_index_files "$1" ...
> (Here, "$1" == "")
> 2. __git_index_files "$1" ... CALLS
> __git_ls_files_helper "$root" "$1" ...
> (Here, "$1" == "", again)
> 3. __git_ls_files_helper "$root" "$1" CALLS
> __git -C "$1" -c core.quotePath=false ls-files
> --exclude-standard $2 -- ...
> (Note that $2 is unquoted, and since it's empty, it disappears)
>
> It seems like it'd be better to pass an explicit "" to
> __git_complete_index_file than to implicitly get it.
Good spot. All the other callers pass an argument.
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v4 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 6:18 ` Elijah Newren
2026-08-07 11:02 ` D. Ben Knoble
@ 2026-08-07 15:13 ` Junio C Hamano
2026-08-07 15:22 ` Elijah Newren
1 sibling, 1 reply; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 15:13 UTC (permalink / raw)
To: Elijah Newren
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
Elijah Newren <newren@gmail.com> writes:
> On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> When completing arguments for 'git diff', _git_diff() delegates to
>> __git_complete_revlist_file(), which only completes revision
>> references. This is good [*], as mixing both revisions and paths in a
>> single list for the user to pick from is simply too confusing.
>>
>> If no reference matches, or if '--' is given, however, _git_diff()
>> leaves COMPREPLY empty. Bash then falls back to default filename
>> completion in $PWD. This fails when 'git -C <path>' is used because
>> $PWD is not the target repository.
>>
>> Update _git_diff() to use __git_complete_index_file() when '--' is
>> present, or when revision reference completion yields no matching
>> candidates, so that tracked paths are offered as candidates.
>>
>> This changes behavior even in the case where '-C <there>' is not
>> used. The new behavior omits untracked paths from suggestions when
>> no revs match the prefix but matching tracked paths exist, which is
>> more useful in the context of 'git diff'.
>
> I'm looking forward to using this. :-)
>
> [...]
>> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
>> index ccd3b2a372..845fd19f70 100644
>> --- a/contrib/completion/git-completion.bash
>> +++ b/contrib/completion/git-completion.bash
>> @@ -1981,6 +1981,10 @@ _git_diff ()
>> esac
>> __git_complete_revlist_file
>> fi
>> +
>> + if [ ${#COMPREPLY[@]} -eq 0 ]; then
>> + __git_complete_index_file
>> + fi
>> }
>
> Curious; __git_complete_index_file() is documented as "requires 1
> argument", but you pass none here. As far as I can tell, it works
> anyway, but feels like an accident:
>
> 1. __git_complete_index_file CALLS
> __git_index_files "$1" ...
> (Here, "$1" == "")
> 2. __git_index_files "$1" ... CALLS
> __git_ls_files_helper "$root" "$1" ...
> (Here, "$1" == "", again)
> 3. __git_ls_files_helper "$root" "$1" CALLS
> __git -C "$1" -c core.quotePath=false ls-files
> --exclude-standard $2 -- ...
> (Note that $2 is unquoted, and since it's empty, it disappears)
>
> It seems like it'd be better to pass an explicit "" to
> __git_complete_index_file than to implicitly get it.
OK. It feels a bit strange as an API for the function to insist
taking one and only one option, which forces the caller to do
__git_complete_index_file "--cached --others --directory"
when the intention clearly is "we take zero or more options that we
pass to ls-files", which would have been more obvious if the above
were written as three separate parameters, but I'll do as Romans in
the (hopefully small and final) reroll.
Thanks.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v4 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 15:13 ` Junio C Hamano
@ 2026-08-07 15:22 ` Elijah Newren
0 siblings, 0 replies; 30+ messages in thread
From: Elijah Newren @ 2026-08-07 15:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
On Fri, Aug 7, 2026 at 8:13 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> > On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
> >>
> >> When completing arguments for 'git diff', _git_diff() delegates to
> >> __git_complete_revlist_file(), which only completes revision
> >> references. This is good [*], as mixing both revisions and paths in a
> >> single list for the user to pick from is simply too confusing.
> >>
> >> If no reference matches, or if '--' is given, however, _git_diff()
> >> leaves COMPREPLY empty. Bash then falls back to default filename
> >> completion in $PWD. This fails when 'git -C <path>' is used because
> >> $PWD is not the target repository.
> >>
> >> Update _git_diff() to use __git_complete_index_file() when '--' is
> >> present, or when revision reference completion yields no matching
> >> candidates, so that tracked paths are offered as candidates.
> >>
> >> This changes behavior even in the case where '-C <there>' is not
> >> used. The new behavior omits untracked paths from suggestions when
> >> no revs match the prefix but matching tracked paths exist, which is
> >> more useful in the context of 'git diff'.
> >
> > I'm looking forward to using this. :-)
> >
> > [...]
> >> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> >> index ccd3b2a372..845fd19f70 100644
> >> --- a/contrib/completion/git-completion.bash
> >> +++ b/contrib/completion/git-completion.bash
> >> @@ -1981,6 +1981,10 @@ _git_diff ()
> >> esac
> >> __git_complete_revlist_file
> >> fi
> >> +
> >> + if [ ${#COMPREPLY[@]} -eq 0 ]; then
> >> + __git_complete_index_file
> >> + fi
> >> }
> >
> > Curious; __git_complete_index_file() is documented as "requires 1
> > argument", but you pass none here. As far as I can tell, it works
> > anyway, but feels like an accident:
> >
> > 1. __git_complete_index_file CALLS
> > __git_index_files "$1" ...
> > (Here, "$1" == "")
> > 2. __git_index_files "$1" ... CALLS
> > __git_ls_files_helper "$root" "$1" ...
> > (Here, "$1" == "", again)
> > 3. __git_ls_files_helper "$root" "$1" CALLS
> > __git -C "$1" -c core.quotePath=false ls-files
> > --exclude-standard $2 -- ...
> > (Note that $2 is unquoted, and since it's empty, it disappears)
> >
> > It seems like it'd be better to pass an explicit "" to
> > __git_complete_index_file than to implicitly get it.
>
> OK. It feels a bit strange as an API for the function to insist
> taking one and only one option, which forces the caller to do
>
> __git_complete_index_file "--cached --others --directory"
>
> when the intention clearly is "we take zero or more options that we
> pass to ls-files", which would have been more obvious if the above
> were written as three separate parameters, but I'll do as Romans in
> the (hopefully small and final) reroll.
>
> Thanks.
Yeah, I don't disagree. I would be equally happy with an update to
the __git_complete_index_file function to change the comment and
explain what the first argument, if given, means (making it clear that
no arguments are okay); but without either that or having your new
caller pass an argument, the inconsistency between the documentation
and this new caller felt like an issue someone might trip over in the
future.
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v4 3/3] completion: 'git diff' completes untracked paths as a last resort
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
2026-08-07 1:38 ` [PATCH v4 1/3] completion: no-op refactoring of diff completion Junio C Hamano
2026-08-07 1:38 ` [PATCH v4 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
@ 2026-08-07 1:38 ` Junio C Hamano
2026-08-07 6:31 ` [PATCH v4 0/3] completion of 'git [-C <dir>] diff' Elijah Newren
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 1:38 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
We taught 'git diff' to first try to complete revisions (unless '--'
is present on the command line) and, failing that, to complete
tracked paths. If this yields nothing, it lets the Bash default,
which offers paths in $PWD, kick in.
Teach it to complete untracked paths before giving up and letting
the Bash default kick in. With this change,
$ git -C another-directory diff un<TAB>
finds the 'untracked' file in another-directory and offers it as a
completion candidate.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 ++++
t/t9902-completion.sh | 22 ++++++++++++++++++++--
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 845fd19f70..7741789e41 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1985,6 +1985,10 @@ _git_diff ()
if [ ${#COMPREPLY[@]} -eq 0 ]; then
__git_complete_index_file
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file "--others --directory"
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index adfaf414fd..eea4bdbb7e 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2664,6 +2664,7 @@ test_expect_success 'setup for integration tests' '
echo more >file2 &&
git add file1 file2 &&
echo untracked >file3 &&
+ echo untracked >ufile &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -2726,21 +2727,38 @@ test_expect_success 'git diff completes tracked paths when no refs match' '
EOF
'
+test_expect_success 'git diff [--] completes untracked paths, too' '
+ # there is no ref or tracked path that begin with u
+ test_completion "git diff u" <<-\EOF &&
+ ufile
+ EOF
+ test_completion "git diff -- u" <<-\EOF
+ ufile
+ EOF
+'
+
test_expect_success 'git -C <path> diff completes in the specified repo' '
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
- # otherfile is tracked, oops is untracked
+ # otherfile is tracked, oops and ufile are untracked
echo content >repo-for-diff/otherfile &&
git -C repo-for-diff add otherfile &&
git -C repo-for-diff commit -m otherfile &&
echo untracked >repo-for-diff/oops &&
+ echo untracked >repo-for-diff/ufile &&
test_completion "git -C repo-for-diff diff o" <<-\EOF &&
otherfile
EOF
- test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF &&
otherfile
EOF
+ test_completion "git -C repo-for-diff diff u" <<-\EOF &&
+ ufile
+ EOF
+ test_completion "git -C repo-for-diff diff -- u" <<-\EOF
+ ufile
+ EOF
'
test_expect_success 'show completes all refs' '
--
2.55.0-655-gb2c071042d
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v4 0/3] completion of 'git [-C <dir>] diff'
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
` (2 preceding siblings ...)
2026-08-07 1:38 ` [PATCH v4 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
@ 2026-08-07 6:31 ` Elijah Newren
2026-08-07 11:05 ` D. Ben Knoble
3 siblings, 1 reply; 30+ messages in thread
From: Elijah Newren @ 2026-08-07 6:31 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> The primary motivation for this topic is that the command-line
> completion of 'git diff' does not handle paths (unlike 'git status'
> and 'git add') and instead relies on the default behavior of Bash
> command-line completion, which completes files in $PWD; this does
> not work at all with the '-C <directory>' option.
>
> This series teaches the completion machinery to complete revisions
> (unless '--' exists), then tracked paths, and then untracked paths,
> before letting the Bash default kick in. This way, we correctly
> complete 'git diff' command line even when '-C <directory>' is in
> effect.
Nice. :-)
> The tests are the only changes relative to v2. In the step where
> tracked paths are completed, v2 did not demonstrate that untracked
> ones are *not* completed at the same time. Now we do by having
> untracked 'file3' next to 'file1' and 'file2' that are tracked. In
> the last step, we demonstrate untracked paths that do not share
> prefix with refs or tracked paths are completed, with or without the
> "-C <dir>" option.
I found this paragraph slightly hard to parse. I think this means the same as:
Only the tests changed since v2. The tracked-paths step now also
shows that untracked paths are not mixed in: an untracked 'file3'
next to tracked 'file1'/'file2' means "git diff f" completes only the
latter two. The untracked-paths step shows that an untracked path
which shares no prefix with a ref or tracked path still completes,
with or without "-C ".
(Not that it matters, since this is just the cover letter, but...)
> 1/3: completion: no-op refactoring of diff completion
> 2/3: completion: complete tracked paths for 'git diff'
> 3/3: completion: 'git diff' completes untracked paths as a last
> resort
I found two minor nits in the first two patches, and didn't spot any
issues with the third patch.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH v4 0/3] completion of 'git [-C <dir>] diff'
2026-08-07 6:31 ` [PATCH v4 0/3] completion of 'git [-C <dir>] diff' Elijah Newren
@ 2026-08-07 11:05 ` D. Ben Knoble
0 siblings, 0 replies; 30+ messages in thread
From: D. Ben Knoble @ 2026-08-07 11:05 UTC (permalink / raw)
To: Elijah Newren
Cc: Junio C Hamano, git, Philippe Blain, Britton Leo Kerin,
Rubén Justo, Patrick Steinhardt, SZEDER Gábor
On Fri, Aug 7, 2026 at 2:32 AM Elijah Newren <newren@gmail.com> wrote:
>
> On Thu, Aug 6, 2026 at 6:38 PM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > The primary motivation for this topic is that the command-line
> > completion of 'git diff' does not handle paths (unlike 'git status'
> > and 'git add') and instead relies on the default behavior of Bash
> > command-line completion, which completes files in $PWD; this does
> > not work at all with the '-C <directory>' option.
> >
> > This series teaches the completion machinery to complete revisions
> > (unless '--' exists), then tracked paths, and then untracked paths,
> > before letting the Bash default kick in. This way, we correctly
> > complete 'git diff' command line even when '-C <directory>' is in
> > effect.
>
> Nice. :-)
>
> > The tests are the only changes relative to v2. In the step where
> > tracked paths are completed, v2 did not demonstrate that untracked
> > ones are *not* completed at the same time. Now we do by having
> > untracked 'file3' next to 'file1' and 'file2' that are tracked. In
> > the last step, we demonstrate untracked paths that do not share
> > prefix with refs or tracked paths are completed, with or without the
> > "-C <dir>" option.
>
> I found this paragraph slightly hard to parse. I think this means the same as:
>
> Only the tests changed since v2. The tracked-paths step now also
> shows that untracked paths are not mixed in: an untracked 'file3'
> next to tracked 'file1'/'file2' means "git diff f" completes only the
> latter two. The untracked-paths step shows that an untracked path
> which shares no prefix with a ref or tracked path still completes,
> with or without "-C ".
>
> (Not that it matters, since this is just the cover letter, but...)
>
> > 1/3: completion: no-op refactoring of diff completion
> > 2/3: completion: complete tracked paths for 'git diff'
> > 3/3: completion: 'git diff' completes untracked paths as a last
> > resort
>
> I found two minor nits in the first two patches, and didn't spot any
> issues with the third patch.
Yep, modulo Elijah's comments (many eyes, thanks!) I'm happy with this round.
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v5 0/3] completion of 'git [-C <dir>] diff'
2026-08-03 0:58 [PATCH] completion: complete tracked paths for 'git diff' Junio C Hamano
` (4 preceding siblings ...)
2026-08-07 1:38 ` [PATCH v4 " Junio C Hamano
@ 2026-08-07 16:19 ` Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 1/3] completion: no-op refactoring of diff completion Junio C Hamano
` (3 more replies)
5 siblings, 4 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 16:19 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
The primary motivation for this topic is that the command-line
completion of 'git diff' does not handle paths (unlike 'git status'
and 'git add') and instead relies on the default behavior of Bash
command-line completion, which completes files in $PWD; this does
not work at all with the '-C <directory>' option.
This series teaches the completion machinery to complete revisions
(unless '--' exists), then tracked paths, and then untracked paths,
before letting the Bash default kick in. This way, we correctly
complete 'git diff' command line even when '-C <directory>' is in
effect.
The v5 iteration addresses two points identified by Elijah in v4.
Hopefully this will be the "small and final" reroll.
1/3: completion: no-op refactoring of diff completion
2/3: completion: complete tracked paths for 'git diff'
3/3: completion: 'git diff' completes untracked paths as a last
resort
contrib/completion/git-completion.bash | 69 +++++++++++++++-----------
t/t9902-completion.sh | 59 ++++++++++++++++++++++
2 files changed, 100 insertions(+), 28 deletions(-)
Range-diff against v4:
1: d3c51c042c ! 1: 8295035d13 completion: no-op refactoring of diff completion
@@ contrib/completion/git-completion.bash: __git_diff_difftool_options="--cached --
- --*)
- __gitcomp "$__git_diff_difftool_options"
- return
+- ;;
+- esac
+- __git_complete_revlist_file
+ if ! __git_has_doubledash; then
+ case "$cur" in
+ --diff-algorithm=*)
+ __gitcomp "$__git_diff_algorithms" \
+ "" "${cur##--diff-algorithm=}"
+ return
- ;;
-- esac
-- __git_complete_revlist_file
++ ;;
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" \
+ "" "${cur##--submodule=}"
2: c3658d6ca2 ! 2: dbb14298c1 completion: complete tracked paths for 'git diff'
@@ contrib/completion/git-completion.bash: _git_diff ()
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
-+ __git_complete_index_file
++ __git_complete_index_file ""
+ fi
}
3: ba5dc6f164 ! 3: d661a1a5dd completion: 'git diff' completes untracked paths as a last resort
@@ Commit message
## contrib/completion/git-completion.bash ##
@@ contrib/completion/git-completion.bash: _git_diff ()
if [ ${#COMPREPLY[@]} -eq 0 ]; then
- __git_complete_index_file
+ __git_complete_index_file ""
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
--
2.55.0-655-g8b87133eb9
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH v5 1/3] completion: no-op refactoring of diff completion
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
@ 2026-08-07 16:19 ` Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
` (2 subsequent siblings)
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 16:19 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
The "git diff" completion function punts very early when it sees
"--" on the command line, since it is a sign that options or
revisions can appear and the current completion does not need to do
anything "git diff" specific. By returning, it lets Bash default
action that completes the names of the files in $PWD to kick in.
In preparation for the next step to change what happens when we
"punt", arrange the code flow to avoid this early return. The
behaviour at this step is unchanged, but the control flow just
falls straight to the end.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 63 ++++++++++++++------------
1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e875787710..a61b6ed59a 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1947,35 +1947,40 @@ __git_diff_difftool_options="--cached --staged
_git_diff ()
{
- __git_has_doubledash && return
-
- case "$cur" in
- --diff-algorithm=*)
- __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
- return
- ;;
- --submodule=*)
- __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
- return
- ;;
- --color-moved=*)
- __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
- return
- ;;
- --color-moved-ws=*)
- __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
- return
- ;;
- --ws-error-highlight=*)
- __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
- return
- ;;
- --*)
- __gitcomp "$__git_diff_difftool_options"
- return
- ;;
- esac
- __git_complete_revlist_file
+ if ! __git_has_doubledash; then
+ case "$cur" in
+ --diff-algorithm=*)
+ __gitcomp "$__git_diff_algorithms" \
+ "" "${cur##--diff-algorithm=}"
+ return
+ ;;
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" \
+ "" "${cur##--submodule=}"
+ return
+ ;;
+ --color-moved=*)
+ __gitcomp "$__git_color_moved_opts" \
+ "" "${cur##--color-moved=}"
+ return
+ ;;
+ --color-moved-ws=*)
+ __gitcomp "$__git_color_moved_ws_opts" \
+ "" "${cur##--color-moved-ws=}"
+ return
+ ;;
+ --ws-error-highlight=*)
+ __gitcomp "$__git_ws_error_highlight_opts" \
+ "" "${cur##--ws-error-highlight=}"
+ return
+ ;;
+ --*)
+ __gitcomp "$__git_diff_difftool_options"
+ return
+ ;;
+ esac
+ __git_complete_revlist_file
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
--
2.55.0-655-g8b87133eb9
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v5 2/3] completion: complete tracked paths for 'git diff'
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 1/3] completion: no-op refactoring of diff completion Junio C Hamano
@ 2026-08-07 16:19 ` Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
2026-08-07 16:53 ` [PATCH v5 0/3] completion of 'git [-C <dir>] diff' Elijah Newren
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 16:19 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
When completing arguments for 'git diff', _git_diff() delegates to
__git_complete_revlist_file(), which only completes revision
references. This is good [*], as mixing both revisions and paths in a
single list for the user to pick from is simply too confusing.
If no reference matches, or if '--' is given, however, _git_diff()
leaves COMPREPLY empty. Bash then falls back to default filename
completion in $PWD. This fails when 'git -C <path>' is used because
$PWD is not the target repository.
Update _git_diff() to use __git_complete_index_file() when '--' is
present, or when revision reference completion yields no matching
candidates, so that tracked paths are offered as candidates.
This changes behavior even in the case where '-C <there>' is not
used. The new behavior omits untracked paths from suggestions when
no revs match the prefix but matching tracked paths exist, which is
more useful in the context of 'git diff'.
When run outside the working tree of a repository, or when nothing
matches from revisions or tracked paths, Bash still falls back to
default filename completion in $PWD, so such a use case would be
just like completing paths for any 'diff' command, rather than for
'git diff'.
[Footnote]
* In https://lore.kernel.org/git/al%2Fw2qgBfhe9qMg6@szeder.dev/
SZEDER made the same argument for "git send-email 0<TAB>".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 +++
t/t9902-completion.sh | 39 ++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index a61b6ed59a..76181e8714 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1981,6 +1981,10 @@ _git_diff ()
esac
__git_complete_revlist_file
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file ""
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 9ae3c48ebd..55361a89e1 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2712,6 +2712,45 @@ test_expect_success 'git -C <path> checkout uses the right repo' '
EOF
'
+test_expect_success 'git diff completes tracked paths when no refs match' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git diff -- completes tracked paths' '
+ # file1 and file2 are tracked but file3 is not
+ test_completion "git diff -- f" <<-\EOF
+ file1
+ file2
+ EOF
+'
+
+test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ echo untracked >repo-for-diff/oops &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff o" <<-\EOF
+ otherfile
+ EOF
+'
+
+test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo' '
+ test_when_finished "rm -rf repo-for-diff" &&
+ git init repo-for-diff &&
+ echo content >repo-for-diff/otherfile &&
+ git -C repo-for-diff add otherfile &&
+ git -C repo-for-diff commit -m otherfile &&
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ otherfile
+ EOF
+'
+
test_expect_success 'show completes all refs' '
test_completion "git show m" <<-\EOF
main Z
--
2.55.0-655-g8b87133eb9
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v5 3/3] completion: 'git diff' completes untracked paths as a last resort
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 1/3] completion: no-op refactoring of diff completion Junio C Hamano
2026-08-07 16:19 ` [PATCH v5 2/3] completion: complete tracked paths for 'git diff' Junio C Hamano
@ 2026-08-07 16:19 ` Junio C Hamano
2026-08-07 16:53 ` [PATCH v5 0/3] completion of 'git [-C <dir>] diff' Elijah Newren
3 siblings, 0 replies; 30+ messages in thread
From: Junio C Hamano @ 2026-08-07 16:19 UTC (permalink / raw)
To: git
Cc: Philippe Blain, Britton Leo Kerin, Elijah Newren,
Rubén Justo, Patrick Steinhardt, D. Ben Knoble,
SZEDER Gábor
We taught 'git diff' to first try to complete revisions (unless '--'
is present on the command line) and, failing that, to complete
tracked paths. If this yields nothing, it lets the Bash default,
which offers paths in $PWD, kick in.
Teach it to complete untracked paths before giving up and letting
the Bash default kick in. With this change,
$ git -C another-directory diff un<TAB>
finds the 'untracked' file in another-directory and offers it as a
completion candidate.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
contrib/completion/git-completion.bash | 4 ++++
t/t9902-completion.sh | 22 +++++++++++++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 76181e8714..d35b4f3024 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1985,6 +1985,10 @@ _git_diff ()
if [ ${#COMPREPLY[@]} -eq 0 ]; then
__git_complete_index_file ""
fi
+
+ if [ ${#COMPREPLY[@]} -eq 0 ]; then
+ __git_complete_index_file "--others --directory"
+ fi
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 55361a89e1..85f92c552d 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2663,6 +2663,7 @@ test_expect_success 'setup for integration tests' '
echo content >file1 &&
echo more >file2 &&
git add file1 file2 &&
+ echo untracked >ufile &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -2728,6 +2729,15 @@ test_expect_success 'git diff -- completes tracked paths' '
EOF
'
+test_expect_success 'git diff [--] completes untracked paths, too' '
+ test_completion "git diff u" <<-\EOF &&
+ ufile
+ EOF
+ test_completion "git diff -- u" <<-\EOF
+ ufile
+ EOF
+'
+
test_expect_success 'git -C <path> diff completes tracked paths in specified repo' '
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
@@ -2744,11 +2754,21 @@ test_expect_success 'git -C <path> diff -- completes pathspecs in specified repo
test_when_finished "rm -rf repo-for-diff" &&
git init repo-for-diff &&
echo content >repo-for-diff/otherfile &&
+ echo untracked >repo-for-diff/untracked &&
git -C repo-for-diff add otherfile &&
git -C repo-for-diff commit -m otherfile &&
- test_completion "git -C repo-for-diff diff -- o" <<-\EOF
+ test_completion "git -C repo-for-diff diff o" <<-\EOF &&
+ otherfile
+ EOF
+ test_completion "git -C repo-for-diff diff -- o" <<-\EOF &&
otherfile
EOF
+ test_completion "git -C repo-for-diff diff u" <<-\EOF &&
+ untracked
+ EOF
+ test_completion "git -C repo-for-diff diff -- u" <<-\EOF
+ untracked
+ EOF
'
test_expect_success 'show completes all refs' '
--
2.55.0-655-g8b87133eb9
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v5 0/3] completion of 'git [-C <dir>] diff'
2026-08-07 16:19 ` [PATCH v5 " Junio C Hamano
` (2 preceding siblings ...)
2026-08-07 16:19 ` [PATCH v5 3/3] completion: 'git diff' completes untracked paths as a last resort Junio C Hamano
@ 2026-08-07 16:53 ` Elijah Newren
3 siblings, 0 replies; 30+ messages in thread
From: Elijah Newren @ 2026-08-07 16:53 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Philippe Blain, Britton Leo Kerin, Rubén Justo,
Patrick Steinhardt, D. Ben Knoble, SZEDER Gábor
On Fri, Aug 7, 2026 at 9:19 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> The primary motivation for this topic is that the command-line
> completion of 'git diff' does not handle paths (unlike 'git status'
> and 'git add') and instead relies on the default behavior of Bash
> command-line completion, which completes files in $PWD; this does
> not work at all with the '-C <directory>' option.
>
> This series teaches the completion machinery to complete revisions
> (unless '--' exists), then tracked paths, and then untracked paths,
> before letting the Bash default kick in. This way, we correctly
> complete 'git diff' command line even when '-C <directory>' is in
> effect.
>
> The v5 iteration addresses two points identified by Elijah in v4.
> Hopefully this will be the "small and final" reroll.
Looks good to me!
^ permalink raw reply [flat|nested] 30+ messages in thread