git ls-files answers the question git status only gestures at: exactly which paths Git has in its index right now. By the end you will have a small set of commands for three separate questions: what Git tracks, what has changed in the working tree, and which files are hidden by ignore rules. The examples use Git 2.43.0, from the installed git-man package version 1:2.43.0-1ubuntu7.3.
Allow about fifteen minutes. You need Git and an existing repository. Everything below is read-only unless you run a command that changes the repository yourself. No command needs sudo: elevated privileges only make permissions harder to diagnose, and they do not reveal files Git cannot read.
Run this from the repository you want to inspect:
$ git --version
git version 2.43.0
$ git rev-parse --show-toplevel
/path/to/project
The second command doubles as a checkpoint. If it says the current directory is not a Git repository, change into the correct checkout rather than pointing the inventory commands at a guessed path.
git ls-files reads Git's index and, when you ask, combines it with the working tree. With no selection option its default is the cached view: paths currently recorded in the index. That means it lists tracked paths, including a tracked file that has since been deleted from disk.
Start with the default form:
$ git ls-files
.gitignore
src/code.txt
tracked.txt
Names and order will differ in your repository. This is one pathname per line, not a report on whether each file is clean: a modified tracked file still appears, because its indexed version is still there. Put a path after -- to narrow the query and make the option boundary explicit:
$ git ls-files -- src/
src/code.txt
Path arguments are matched against paths in the index. They do not turn this into a general filesystem search.
Use a separate selection flag for each working-tree state you care about:
$ git ls-files --modified --deleted --others --exclude-standard
new.txt
src/code.txt
tracked.txt
.gitignore, .git/info/exclude and your global excludes.Drop --exclude-standard and an ignored file can turn up among the others: --others does not switch on standard ignore rules by itself, and that mismatch is a common source of noisy scripts.
Checkpoint: compare the result with Git's human-readable status view when you are investigating a change:
$ git status --short
M tracked.txt
D src/code.txt
?? new.txt
The two commands answer related questions in different formats. Status is usually easier for a person to read; ls-files earns its keep when you need a precise index or pathname query instead.
Pass --tag to place a single status character before each path:
$ git ls-files --tag --modified --deleted --others --exclude-standard
? new.txt
C src/code.txt
C tracked.txt
The tag explains why the path was picked. ? means untracked; C means a tracked file has an unstaged change. A deleted tracked path is also tagged C here, because an unstaged deletion counts as an unstaged modification. The exact set depends on the flags and the repository state.
Do not parse tagged output as a stable machine protocol if you need richer status detail. The installed manual points instead to git status --porcelain or git diff-files --name-status for scripting.
Use --stage when you need the mode, object name and index stage rather than just the path:
$ git ls-files --stage -- tracked.txt
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 tracked.txt
The object ID will differ on your machine. The normal stage is 0. During an unresolved merge, the index can hold stages 1, 2 and 3 for one path; add --unmerged to see only those unresolved entries, which implies staged detail and suppresses the ordinary cached listing.
Need only selected fields? Use the format option:
$ git ls-files --format='%(objectname) %(path)' -- tracked.txt
5626abf0f72e58d7a153368ba57db4c673c0e171 tracked.txt
Supported field names include objectmode, objecttype, objectname, objectsize, stage, end-of-line fields and path. Do not combine --format with options such as --stage, --others, --unmerged, --tag or --eol: Git rejects the incompatible combinations outright.
When line-ending conversion is confusing, ask Git to show the index value, working-tree value and applicable attribute together:
$ git ls-files --eol -- tracked.txt src/code.txt
i/lf w/lf attr/ tracked.txt
i/lf w/none attr/ src/code.txt
The output is host-specific. i/ describes the index, w/ describes the working tree, and attr/ shows the relevant attribute. A missing or unreadable working-tree file can produce an empty or different working-tree value.
For scripts, never assume a pathname is safe to split on newlines. By default Git quotes unusual characters according to core.quotePath. Add --null (short form -z) to emit raw names separated by NUL bytes instead:
$ git ls-files --others --exclude-standard --null |
while IFS= read -r -d '' path; do
printf 'untracked: %s\n' "$path"
done
Keep the NUL delimiter all the way through the consumer. A command that converts the stream back to newline-separated text throws away the protection you just asked for.
To list ignored files, combine --ignored with an explicit cached or other view and an exclude source:
$ git ls-files --others --ignored --exclude-standard
debug.log
--ignored on its own is not enough: the manual requires either --cached or --others, plus at least one exclude option. For a repository's standard rules, --exclude-standard is normally the least surprising choice. To test one extra pattern without touching any ignore file, use a shell wildcard as an exclude instead:
$ git ls-files --others --exclude='*.tmp'
scratch.tmp
Quote the pattern so the shell hands it to Git unchanged. These commands only inspect paths: they do not delete ignored files, add them to the index or alter .gitignore. Be careful with a later command such as git clean, though: that is a separate, destructive operation and should never be assumed from an inventory.
If a path is supposed to be tracked, require an index match and use the exit status in a script:
$ git ls-files --error-unmatch -- path/to/file
error: pathspec 'path/to/file' did not match any file(s) known to git
Did you forget to 'git add'?
$ printf 'exit status: %s\n' "$?"
exit status: 1
The error text is from Git 2.43.0 and can vary slightly by version. The useful contract is the non-zero status when a requested path is missing from the index. Check spelling, repository location and whether the file was ever added, before you go anywhere near ignore rules.
If you are in a subdirectory, output is normally relative to it. Add --full-name when a script needs paths relative to the repository root instead:
$ cd src
$ git ls-files --full-name -- code.txt
src/code.txt
Do not confuse a relative display path with a different index entry. --full-name changes the presentation, not what Git tracks.
--others does not enable standard ignores on its own.-z when unusual names are possible.