I want to find all markdown files in the base directory of a specified branch. git ls-files
was suggested to me, but the documentation doesn't mention specifying a branch. I want to be able to do this without checking out that branch. Can this be done?

- 260
- 1
- 11
-
2https://git-scm.com/docs/git-ls-tree – matt Apr 06 '19 at 02:39
-
I don't see a way to filter based on file extension. `git ls-files *.md' showed me all markdown files in the current branch, and `git ls-tree --name-only origin/master` shows me all the files in `origin/master`. Will I have to filter myself, or is there a flag I can use? – Pi Fisher Apr 06 '19 at 02:49
2 Answers
git ls-files
examines files in the index or the work-tree (or both). Since the current index and work-tree contents generally reflect the current commit as extracted from the tip of the current branch, it won't work this way. But there is a workaround.
As VonC notes, git ls-tree
examines a tree object, such as that stored in a commit. However, git ls-tree
does not accept pathspec arguments such as **/*.md
.
The workaround is to read the commit of interest into a temporary index. To do that cleanly, use mktemp
to make a temporary file, then remove the temporary file and use git read-tree
to re-create the file as valid a temporary index containing the image of the commit you wish to inspect, which you can then inspect with git ls-files
. For example:
$ cd git
$ sh -c 'export GIT_INDEX_FILE=$(mktemp); rm $GIT_INDEX_FILE; git read-tree e83c5163316f89bfbde7d9ab23ca2e25604af290; git ls-files -- "*.h" "**/*.h"; rm $GIT_INDEX_FILE'
cache.h
$ sh -c 'export GIT_INDEX_FILE=$(mktemp); rm $GIT_INDEX_FILE; git read-tree origin/master; git ls-files -- "*.md"; rm $GIT_INDEX_FILE'
.github/CONTRIBUTING.md
.github/PULL_REQUEST_TEMPLATE.md
README.md
contrib/vscode/README.md
(Note: the sh -c 'export ...; cmd1; cmd2; cmd3'
is all required, although if you're already using a POSIX-compatible shell you can replace sh -c
with parentheses for a subshell. We need the environment variable GIT_INDEX_FILE
to stay set for just those commands. If your mktemp
has -u
, consider using that instead of the first rm
. See also How portable is mktemp(1)?)

- 448,244
- 59
- 642
- 775
-
I don't understand the point of creating a temporary file and immediately removing it. Then you remove `$GIT_INDEX_FILE` again after `git read-tree`, which should give you an error, because you already removed that temporary file in the first invocation of `rm $GIT_INDEX_FILE`. Could you explain this a little further? – EarthIsHome Jun 09 '22 at 13:13
-
1@EarthIsHome: Try it without removing it and observe the effect. (Seriously, try it. Is this a bug in Git? I would argue that it could be considered one.) Note that if your `mktemp` has `-u` you can use this flag instead of a separate initial `rm`. Also, consider as an exercise answering the question: "what file(s) does git read-tree create". – torek Jun 09 '22 at 15:41
-
I did try it. It changed my index to what I `git read-tree` without changing my HEAD or files in my working tree. I `reset --hard` back to the branch that my HEAD was pointed at. – EarthIsHome Jun 09 '22 at 16:02
-
I guess, I don't understand how git is using the temporary file created by `mktemp`. – EarthIsHome Jun 09 '22 at 16:03
-
1You must have left out the `export` or not run this as a single separate sub-shell command. The environment variable `$GIT_INDEX_FILE` names a file that Git should use as a temporary index, but this file must either *not exist* (in which case Git creates a valid index), or else must *contain a valid index* (in which case Git uses that as the starting point for operations like read-tree). An empty (zero-byte) file is not a valid index as it is missing its checksum. – torek Jun 09 '22 at 16:10
-
Ah, not understanding `GIT_INDEX_FILE` is the source of my misunderstanding. This makes sense now! Thank you. And yes, you were right, I wasn't in a subshell; I was just playing around in my main shell. – EarthIsHome Jun 09 '22 at 16:29
If you are using git ls-tree
, you might need to filter the output yourself, to grep what you need.
As mentioned in this thread back in 2012:
There are two uses of "pattern" there, and the former is probably OK but the latter is indeed misleading.
Back when we wrote this command, there were two distinct "pathspec" families, and
ls-tree
belong to the one with "diff
" family that did not take anything but "leading path pattern" (the other one from "ls-files
" and "grep
" family allowed wildcards). Giving a wildcard to the command in the "ls-tree
/diff
" family was never supported.If you change
ls-tree
to use its paths arguments as pathspec, you will break backward compatibility.

- 1,262,500
- 529
- 4,410
- 5,250