35

I'm using msysgit (1.7.9), and I'm looking for the right invocation of the git ls-files command to show just the (tracked) files and directories at the current level, either from the index, or the current working directory if that's easier.

Essentially it would give a directory listing similar that that you would see on Github. Coming from Windows, I'm not too familiar with the right way of doing the globbing(?).

Philip Oakley
  • 13,333
  • 9
  • 48
  • 71

7 Answers7

30

(late edit for a feature added in Git 1.8.5, after the question and answer were written:)

Git's pathspecs ordinarily match * with any path substring, including / separators, but you can use shell pathname-matching conventions by adding a magic :(glob) prefix. So to list just the files in the current directory,

git ls-files ':(glob)*'   # just the files in the current directory
git ls-files ':(glob)test/*' # and so on

As a side note, you can do many more things with those magic prefixes, chase that link.

You can also turn on shell-style globbing with the --glob-pathspecs option on the Git command itself, so

$ alias ggit='git --glob-pathspecs'
$ # and a personal favorite:
$ git config --global alias.ls 'ls-files --exclude-standard'
$ # then whenever
$ ggit ls '*'     # note the quoting to bypass the shell's globbing,

Original answer, still works:

I think you want git ls-tree HEAD sed'd to taste. The second word of ls-tree's output will be tree for directories, blob for files, commit for submodules, the filename is everything after the ascii tab.

Edit: adapting from @iegik's comment and to better fit the question as asked,

git ls-files . | sed s,/.*,/, | uniq

will list the indexed files starting at the current level and collapse directories to their first component.

Further edit: another way to do it is

git ls-tree `git write-tree` .

and you can use git ls-tree's options for some nice seasoning.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • 3
    `git ls-files | awk -F / '{print $1}' | uniq | xargs ls -dl --color=auto` – iegik Jul 23 '16 at 08:34
  • @iegik This is much better. I'd `sed s,/.*,,` instead of the awk, just 'cause it's shorter. Make this an answer, please? I'll bounty it. – jthill Jul 23 '16 at 10:00
  • Doesn't work on macOS — `ls: illegal option -- -`. I think you want, at least on macOS, `… | xargs 'ls' -Gdl` — someone else can check if that works on Linux, too? – ELLIOTTCABLE Jul 21 '17 at 23:38
  • Also, how to set this as a git alias? Non-shell aliases don't seem to support piping, but shell-aliases are run at the repository root, instead of the current directory (`git config --global alias.ls "\!git ls-files | awk -F / '{print $1}' | uniq | xargs 'ls' -Gdl"` is a start, but …) – ELLIOTTCABLE Jul 21 '17 at 23:47
22

I believe git ls-tree --name-only [branch] will do what you're looking for.

David Cain
  • 16,484
  • 14
  • 65
  • 75
  • 3
    Thanks, that's 95% the way there. Your `git ls-tree --name-only [branch]` lists both the directories and files in the same list (i.e. no trailing `/` for directories). I found `git ls-tree -d --name-only [branch]` will list just the directories. Part of the 'problem' is to spot submodule directories. – Philip Oakley May 04 '12 at 18:34
  • This should probably be the accepted answer! That, or it's a very good start towards one. :D – ELLIOTTCABLE Jul 21 '17 at 23:35
  • @PhilipOakley I was trying to do a similar thing, except with `git ls-files` rather than `git ls-tree`. With `git ls-files`, it looks like the equivalent option is `--directory` ( in that command, `-d` is short for `--deleted`). – Tyler Rick Feb 07 '18 at 22:23
7

To just list the files in the current working directory that are tracked by git, I found that the following is several times faster than using git ls-tree...:

ls | grep -f <(git ls-files)

It would take a little messing around with sed if you also wanted to include directories, something along the lines of:

ls | grep -f <(git ls-files | sed 's/\/.*//g' | sort | uniq)  

assuming you don't have any '/' characters in the names of your files. As well as...

ls -a | grep -f <(git ls-files | sed 's/\/.*//g' | sort | uniq)

in order to also list "invisible" (yet-tracked) files.

Alex Gray
  • 16,007
  • 9
  • 96
  • 118
AlexJWR
  • 121
  • 1
  • 5
  • This is nice... I wonder why I cannot add it as an `[alias]`?? `ls-tracked = "! f(){ ls | grep -f <(git ls-files | sed 's/\/.*//g' sort | uniq); }; f"`-> `fatal: bad config file` – Alex Gray Feb 16 '16 at 00:59
6

git ls-tree <tree-ish> is good and all, but I can't figure out how to specify the index as the <tree-ish>. (Although I'm sure there's bound to be some all-caps reference to do just that.)

Anyhow, ls-files implicitly works on the index so I might as well use that:

$ git ls-files | cut -d/ -f1 | uniq

This shows files and directories only in the current directory.

Change cut's -f argument to control depth. For instance, -f-2 (that's dash two) shows files and directories up to two levels deep:

$ git ls-files | cut -d/ -f-2 | uniq

IF you specify the <path> argument to ls-files, make sure to increase -f to accommodate the leading directories:

$ git ls-files foo/bar | cut -d/ -f-3 | uniq
antak
  • 19,481
  • 9
  • 72
  • 80
  • You can get a tree for the index with [`git write-tree`](https://git-scm.com/docs/git-write-tree). – jthill Jul 23 '17 at 02:50
2

I'm surprised this is so hard... but don't get me started on my griping about git.

A variant on jthill's answer seems to be aliasable (hey, I'm a linguist, I have a license to make new words). The variant is

ls -d `git ls-tree HEAD | sed -e "s/^.*\t//"`

This uses 'ls' to format the output, so you get color coding (if you use that), etc. It also works as an alias:

alias gitls='ls -d `git ls-tree HEAD | sed -e "s/^.*\t//"`'

FWIW, you can also alias the recursive command, so that you used the 'ls' formatting (e.g. if your path+filenames aren't too long, you'll get two column output, color coding of executables, etc.)

alias gitls-r='ls `git ls-files`'
Mike Maxwell
  • 547
  • 4
  • 11
  • Smart trick getting coloring through 'ls'. I personally prefer 'ls -ld', but that's me. Instead of piping through sed, you could use the --name-only (probably new) flag. – Uri London Sep 02 '18 at 12:39
  • @Uri: right, --name-only is simpler, thanks! And using -ld is certainly simpler than some of the methods above for getting full info. – Mike Maxwell Sep 03 '18 at 15:41
  • For aliases using `--name-only`, see https://stackoverflow.com/a/57342429/10850071 – baltakatei Mar 05 '21 at 21:24
0

The simplest solution I have found, believe it or not, is to simply cd into the directory you want in your terminal. Running git ls-files in /project/src will give you results from only that directory, versus running it in /project

Not the most technical answer, but hey, it works!

Ryan
  • 150
  • 6
  • is simple and effective =), if you don't have any more folders in that folder you cd into, only issue is that if you have folders and other files will march down down those as well – pelos Aug 12 '21 at 18:59
-1

use this simple bash script for define subdirectory any level

dpolyakov
  • 260
  • 1
  • 3
  • 11