[PATCH] completion: complete tracked paths for 'git diff'
Junio C Hamano <[email protected]> Sun, 02 Aug 2026 17:58:01 -0700
| Newsgroups | org.kernel.vger.git |
|---|---|
| Message-ID | <[email protected]> |
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%[email protected]/ SZEDER made the same argument for "git send-email 0<TAB>". Signed-off-by: Junio C Hamano <[email protected]> --- * 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