Since 2011, as the OP comments, Git supports full filename completion since ~1.8.2.
But with Git 2.18 (Q2 2018), the shell completion (in contrib/
) that gives list of paths have been optimized somewhat.
See commit 78a2d21 (04 Apr 2018) by Clemens Buchacher (drizzd
).
(Merged by Junio C Hamano -- gitster
-- in commit 3a940e9, 25 Apr 2018)
completion
: improve ls-files
filter performance
From the output of ls-files
, we remove all but the leftmost path
component and then we eliminate duplicates. We do this in a while
loop,
which is a performance bottleneck when the number of iterations is large
(e.g. for 60000 files in linux.git
).
$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git
real 0m11.876s
user 0m4.685s
sys 0m6.808s
Replacing the loop with the cut command improves performance
significantly:
$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git
real 0m1.372s
user 0m0.263s
sys 0m0.167s
The measurements were done with Msys2 bash, which is used by Git for
Windows.
When filtering the ls-files
output we take care not to touch absolute
paths. This is redundant, because ls-files
will never output absolute
paths. Remove the unnecessary operations.
The issue was originally reported Git for Windows issue 1533.
The directory traversal code had redundant recursive calls which made its performance characteristics exponential with respect to the depth of the tree, which was corrected with Git 2.27 (Q2 2020).
See commit c0af173, commit 95c11ec, commit 7f45ab2, commit 1684644, commit 8d92fb2, commit 2df179d, commit 0126d14, commit cd129ee, commit 446f46d, commit 7260c7b, commit ce5c61a (01 Apr 2020) by Elijah Newren (newren
).
See commit 0bbd0e8 (01 Apr 2020) by Derrick Stolee (derrickstolee
).
(Merged by Junio C Hamano -- gitster
-- in commit 6eacc39, 29 Apr 2020)
completion
: fix 'git add
' on paths under an untracked directory
Signed-off-by: Elijah Newren
As reported on the git mailing list, since git-2.25,
git add untracked-dir/
has been tab completing to
git add untracked-dir/./
The cause for this was that with commit b9670c1f5e ("dir
: fix checks on common prefix directory", 2019-12-19, Git v2.25.0-rc0 -- merge),
git ls-files -o --directory untracked-dir/
(or the equivalent git -C untracked-dir ls-files -o --directory
) began reporting
untracked-dir/
instead of listing paths underneath that directory.
It may also be worth noting that the real command in question was
git -C untracked-dir ls-files -o --directory '*'
which is equivalent to:
git ls-files -o --directory 'untracked-dir/*'
which behaves the same for the purposes of this issue (the '*
' can match the empty string), but becomes relevant for the proposed fix.
At first, based on the report, I decided to try to view this as a regression and tried to find a way to recover the old behavior without breaking other stuff, or at least breaking as little as possible.
However, in the end, I couldn't figure out a way to do it that wouldn't just cause lots more problems than it solved.
The old behavior was a bug:
- Although older git would avoid cleaning anything with
git clean -f .git
, it would wipe out everything under that directory with git clean -f .git/
.
Despite the difference in command used, this is relevant because the exact same change that fixed clean changed the behavior of ls-files.
- Older git would report different results based solely on presence or absence of a trailing slash for
$SUBDIR
in the command git ls-files -o --directory $SUBDIR
.
- Older git violated the documented behavior of not recursing into directories that matched the pathspec when
--directory
was specified.
- And, after all, commit b9670c1f5e (
dir
: fix checks on common prefix directory, 2019-12-19, Git v2.25.0-rc0) didn't overlook this issue; it explicitly stated that the behavior of the command was being changed to bring it inline with the docs.
(Also, if it helps, despite that commit being merged during the 2.25 series, this bug was not reported during the 2.25 cycle, nor even during most of the 2.26 cycle -- it was reported a day before 2.26 was released.
So the impact of the change is at least somewhat small.)
Instead of relying on a bug of ls-files
in reporting the wrong content, change the invocation of ls-files used by git-completion to make it grab paths one depth deeper.
Do this by changing '$DIR/*
' (match $DIR/
plus 0 or more characters) into '$DIR/?*
' (match $DIR/
plus 1 or more characters).
Note that the '?
' character should not be added when trying to complete a filename (e.g. 'git ls-files -o --directory merge.c?*"' would not correctly return "[
merge.c](https://github.com/git/git/blob/c0af173a136785b3cfad4bd414b2fb10a130760a/merge.c)" when such a file exists), so we have to make sure to add the '
?`' character only in cases where the path specified so far is a directory.
Warning: Git 2.29 (Q4 2020) fixes a regression introduced during 2.27 cycle.
See commit cada730 (20 Jul 2020) by Martin Ågren (none
).
(Merged by Junio C Hamano -- gitster
-- in commit 82fafc7, 30 Jul 2020)
dir
: check pathspecs before returning path_excluded
Reported-by: Andreas Schwab
Reviewed-by: Elijah Newren
Signed-off-by: Martin Ågren
In 95c11ecc73 ("Fix error-prone fill_directory()
API; make it only return matches", 2020-04-01, Git v2.27.0-rc0 -- merge listed in batch #5), we taught fill_directory()
, or more specifically treat_path()
, to check against any pathspecs so that we could simplify the callers.
But in doing so, we added a slightly-too-early return for the "excluded" case. We end up not checking the pathspecs, meaning we return path_excluded
when maybe we should return path_none
. As a result, git status --ignored -- pathspec
(man) might show paths that don't actually match "pathspec
".
Move the "excluded" check down to after we've checked any pathspecs.
Git 2.38 (Q3 2022) fixes another regression introduced during 2.27 cycle, which can impact git-bash completion.
In a non-bare repository, the behavior of Git when the core.worktree
configuration variable points at a directory that has a repository as its subdirectory, regressed in Git 2.27 days.
See commit d6c9a71, commit 2712899 (16 Jun 2022) by Goss Geppert (ggossdev
).
(Merged by Junio C Hamano -- gitster
-- in commit dc6315e, 14 Jul 2022)
dir
: traverse into repository
Signed-off-by: Goss Geppert
Reviewed-by: Elijah Newren
Since 8d92fb2 ("dir
: replace exponential algorithm with a linear one", 2020-04-01, Git v2.27.0-rc0 -- merge listed in batch #5) traversing into a repository's directory tree when the traversal began outside the repository's standard location has failed because the encountered repository was identified as a nested foreign repository.
Prior to this commit, the failure to traverse into a repository's default worktree location was observable from a user's perspective under either of the following conditions (there may be others):
- Set the
core.worktree
location to a parent directory of the default worktree; or
- Use the
--git_dir
option while the working directory is outside
the repository's default worktree location
Under either of these conditions, symptoms of the failure to traverse into the repository's default worktree location include the inability to add files to the index or get a list of untracked files via ls-files
.
This commit adds a check to determine whether a nested repository that is encountered in recursing a path is actually the_repository
.
If so, we simply treat the directory as if it doesn't contain a nested repository.
git status
first, and then know what to type forgit diff
. (But most often I'm usinggitk
for seeing differences.) – Zwinglian