Git development
 help / color / mirror / Atom feed
* [PATCH] completion: complete tracked paths for 'git diff'
@ 2026-08-03  0:58 Junio C Hamano
  2026-08-03  1:07 ` Junio C Hamano
  2026-08-03  5:44 ` SZEDER Gábor
  0 siblings, 2 replies; 3+ messages in thread
From: Junio C Hamano @ 2026-08-03  0:58 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 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.

[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.  The contribution
   by any of them looked more expertly done by whatever I did here.

   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 related	[flat|nested] 3+ 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
  1 sibling, 0 replies; 3+ 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] 3+ 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
  1 sibling, 0 replies; 3+ 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] 3+ messages in thread

end of thread, other threads:[~2026-08-03  5:44 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox