Subject: gripe: bash completion broken for 'diff' from outside working tree

From: Junio C Hamano <[email-masked]>
Date: Thu, 27 Feb 2025 09:46:38 -0800

I chose to CC those who have commits in our tree in the last year
among those who touched contrib/completion/git-completion.bash in
the past 3 years, hoping that they are much much more clueful than
I am in this script.

Let's say I have a checkout of our source files in $HOME/w/git.git
and have a few files modified:

    $ cd $HOME/w/git.git
    $ git diff --name-status
    M       COPYING
    M       Makefile

Going one level up, and trying to run various common commands shows
differing level of completion support.

    $ cd $HOME/w/
    $ ls git.git/CO<TAB>
    CODE_OF_CONDUCT.md  COPYING
    $ git -C git.git add CO<TAB>	# completes to 'COPYING'

This completes to COPYING even though CO is *not* a unique prefix
(as seen in the completion of "ls"), simply because the other
tracked file has no modification and there is no point in adding it.
The completion of "add" seems even nicer.  See.

    $ >git.git/CONTRIBUTING
    $ git -C git.git add CO<TAB>
    CONTRIBUTING  COPYING

It notices that an untracked file CONTRIBUTING could be added, so
includes it in the candidates.  I guess __git_complete_index_file
that is called at the end of _git_add is what has the whole smart
to do this?

"git status" is equally nice.

    $ git -C git.git status CO<TAB>
    CODE_OF_CONDUCT.md  CONTRIBUTING   COPYING

And It is benefiting from the same __git_complete_index_file at the
end of _git_status, I think.

But "git diff" is not.  Among these three commands, this is the one
I most often use (so expect to behave the best).

    $ git -C git.git diff CO<TAB>	# nothing.

A cursory look of __git_complete_revlist_file tells me that it does
not do anything special on the pathspec on the command line.  It
calls __git_complete_refs so it may be aware of other branches, to
wit:

    $ git -C git.git diff ne<TAB>	# completes to 'next'.

but does not care anything about the files.

When you are in the working tree, I think it is helped by the "list
of files relative to the cwd" default completion behaviour kicking
in (i.e. I get the same completion as "ls CO<TAB>").

    $ cd git.git && git diff CO<TAB>
    CODE_OF_CONDUCT.md  CONTRIBUTING   COPYING

I think this behaviour is serviceable; some may argue that omitting
CONTRIBUTING (which is not tracked yet) and CODE_OF_CONDUCT.md
(which has no changes) and directly completing to COPYING would be
nicer, but the users who want to run "git diff" want to know which
paths have modifications, which in turn means they do not know that
CODE_OF_CONDUCT.md has no changes, and may not even know that they
have not yet added CONTRIBUTING yet.  So I am inclined to say that
it is perfectly OK not to omit them.  On the other hand, if it is
easier to find paths out of the index, I am also fine if they are
omitted.

But outside the working tree with the "git -C <go-there> diff",
behaviour is even worse than the behaviour we get from the falling
back to the default.  It is completly broken, since the directory
listing the default behaviour chooses from is from the current
directory and not from the working tree specified by the "-C"
option.

I find this frustration at least twice a day, every day, when I have
do "git -C Meta diff whats-cooking.txt".

Help?