Using git list-files
gives me the directories and the files tracked within. Is there a command like:
git list-directories
or something similar that lists only the tracked non-empty non-recursive directory names?
Using git list-files
gives me the directories and the files tracked within. Is there a command like:
git list-directories
or something similar that lists only the tracked non-empty non-recursive directory names?
git ls-files | xargs -n 1 dirname | uniq
git ls-tree -rt HEAD:./ | awk '{if ($2 == "tree") print $4;}'
If the files may contain spaces, we would have to play around with: Using awk to print all columns from the nth to the last which is not very fun.
-r
makes it recurse, and -t
makes it print trees when recursing, which is turned off by default.
I was unable to use ls-tree
as mentioned at: https://stackoverflow.com/a/20247815/895245 because it is hard to deal with directories that have no files, just other directories.
This method also shows empty trees.
Tested on Git 2.19.0.
The following command (leveraging git ls-files
) returns all paths of directories directly containing committed or staged files.
This command can be executed irrespective of the 'current working directory' it is executed in (i.e., it can be executed in any subdirectory of this git repository, yet will always return paths including those from other subdirectories (instead of only returning paths of only the current subdirectories(that being a characteristic/limitation/caveat/gotcha I noticed with other answers))).
The returned paths are relative to this git repository's root directory.
git ls-files --full-name $(git rev-parse --show-toplevel) | xargs -n 1 dirname | sort --version-sort | uniq | grep --invert-match '^\.$'
Explanation:
git ls-files --full-name ...
: return file paths relative to "starting point".... $(git rev-parse --show-toplevel)
: set the "starting point" to this git repository's root directory. This is important if you want to have all paths, and not only paths scoped to the current working directory this command is executed in.xargs -n 1 dirname
: The previous pipe step has returned committed and staged files. This pipe step returns their containing directory.sort --version-sort
: by default, sort
sorts strings with dots (e.g. .config/foo
) unexpectedly. The --version-sort
option results in a more logical sorting.uniq
: filter away duplicate entries.grep --invert-match '^\.$'
: previous pipe steps created a line entry ".
", signifying that there exists at least one committed or staged file directly in this git repository's root directory. This ".
" entry may or may not be desirable. When not desired, this grep
statement filters it away.Example git repository (located within /home/someuser/git/testrepository/
):
# committed files (listed with "git ls-files /home/someuser/git/testrepository/")
.config/foo.conf
.config/very/nested/deep.txt
README.md
a-directory/a1-file.txt
a-directory/a2-file.txt
z-directory/z-file.txt
z-directory/z-subdirectory/z-subfile.txt
Running aforementioned command from any contained (sub-)directory, e.g. from /home/someuser/git/testrepository/.config/very/
:
$ cd /home/someuser/git/testrepository/.config/very/
$ git ls-files --full-name $(git rev-parse --show-toplevel) | xargs -n 1 dirname | sort --version-sort | uniq | grep --invert-match '^\.$'
.config
.config/very/nested
a-directory
z-directory
z-directory/z-subdirectory
Notice:
.config/very
is not present in the returned list. This is because there doesn't exist any file committed or staged directly in .config/very/
..config/very/nested
is present in the returned list as it (directly) contains the committed file .config/very/nested/deep.txt
.The following command (leveraging git ls-tree
) will return all paths leading towards committed files, including intermediate directories. Different to the previous command, it will not include paths to directories which only contain staged-and-never-committed files:
git ls-tree -rt HEAD --name-only --full-tree | xargs -n 1 dirname | sort --version-sort | uniq | grep --invert-match '^\.$'
This is the output for the previously given example git repository:
$ cd /home/someuser/git/testrepository/.config/very/
$ git ls-tree -rt HEAD --name-only --full-tree | xargs -n 1 dirname | sort --version-sort | uniq | grep --invert-match '^\.$'
.config
.config/very
.config/very/nested
a-directory
z-directory
z-directory/z-subdirectory
Notice:
.config/very
is present in the returned list despite that directory not having a committed file directly in it. It is included because it leads towards a committed file (.config/very/nested/deep.txt
).How about the following solution:
git ls-files | sed -e '/^[^/]*$/d' -e 's|/[^/]*$||' | sort -u