Home / Alt manpages / git-ls-tree(1)

  • git-ls-tree(1)
  • User command
  • linux

Inspect a Commit's Tree with git ls-tree

git ls-tree shows you exactly what Git recorded in a commit, which is not always what you assume is sitting in your working directory. By the end you will have a reliable way to inspect those files, recurse into selected directories, and produce output that scripts can consume. The examples use Git 2.43.0, matching the installed git-ls-tree(1) manual on this machine.

Allow about fifteen minutes. You need Git and an existing repository with at least one commit. These commands only read objects, aside from the temporary repository used in the verification example, and they do not touch branches, the index or the working tree. No elevated privileges required.

1. Check the installed command and choose a tree

Start in the repository you want to inspect. Confirm the version and pick a tree-ish, usually a commit such as HEAD:

$ git --version
git version 2.43.0
$ git rev-parse --show-toplevel
/path/to/repository
$ git rev-parse HEAD
0123456789abcdef0123456789abcdef01234567

The object named by <tree-ish> can be a commit, tag, branch name or any other revision that resolves to a tree. A commit is convenient because Git follows it straight to its top-level tree. Swap in an object ID from your own repository; never use the example value as a real ID.

Checkpoint

If git rev-parse HEAD fails, stop and check you are inside a Git repository with at least one commit. Skip sudo: privilege does not create repository history.

2. List the top level in the default format

Run git ls-tree with the tree-ish and no path:

$ git ls-tree HEAD
100644 blob 4a58007052a65fbc2fc3f910f2855f45a4058e74    README.md
040000 tree 6f59585c86a39f45a7e2043dd5a218a9740ffcfb    docs
040000 tree 01afdf4295719fab3c9b3ebf620f323afc828e5b    src

Each line has the mode, object type, object name, a tab, then the path. IDs and paths will differ in your repository. A regular file is a blob; a directory is a tree. This is the committed snapshot, not a report on uncommitted files sitting in your working directory.

With no path argument, the command lists the root level only. It does not recursively expand every directory, which is useful when you just want the shape of a tree before drilling into a smaller part of it.

3. Recurse into a directory

Use -r for files below directory entries. Add --name-only when object IDs and modes would only get in the way of the pathname list:

$ git ls-tree -r --name-only HEAD src
src/main.txt
src/nested/detail.txt

Paths are read relative to the current working directory. Run this from a subdirectory and src means that subdirectory's src, not necessarily the repository root's. Use --full-tree when the current directory should not limit the lookup:

$ (cd src && git ls-tree --full-tree --name-only HEAD docs src)
docs
src

--full-tree also implies --full-name, so matching paths report from the repository root. The parentheses just keep the directory change inside a subshell: Git does not require them, but they stop your session ending up in a different directory than you started.

4. Select directories without accidentally expanding them

A path argument is a pattern to match, not quite the same as a shell directory argument. Without -r, naming a directory reports the directory entry itself:

$ git ls-tree HEAD src
040000 tree 01afdf4295719fab3c9b3ebf620f323afc828e5b    src
$ git ls-tree -d HEAD
040000 tree 6f59585c86a39f45a7e2043dd5a218a9740ffcfb    docs
040000 tree 01afdf4295719fab3c9b3ebf620f323afc828e5b    src

-d shows only the named tree entries, not their children, which is a quick way to check whether a directory exists in the snapshot. -r does the opposite and descends into sub-trees. -t keeps the tree entry in the output while recursing, and -d implies -t.

Checkpoint

Decide up front whether your output should describe containers or their contents. Reaching for -d when you wanted an inventory of files is a common trap, because the command succeeds while quietly returning only directory entries.

5. Make output suitable for a script

Use a narrow output mode when a script needs one field. --object-only prints object names; --name-only prints paths. You cannot combine these two modes with each other:

$ git ls-tree -r --object-only HEAD src
65b2df87f7df3aeedef04be96703e55ac19c2cfb
af17f6cc87e4d5e4adec0018cbb73d3e2bd008c8

These IDs come from the small verification repository used for these examples, so yours will differ. For a custom record, use --format:

$ git ls-tree --format='%(objecttype) %(objectname) %(path)' HEAD
blob 4a58007052a65fbc2fc3f910f2855f45a4058e74 README.md
tree 6f59585c86a39f45a7e2043dd5a218a9740ffcfb docs
tree 01afdf4295719fab3c9b3ebf620f323afc828e5b src

Available fields in this installed version are objectmode, objecttype, objectname, objectsize or objectsize:padded, and path. Do not combine --format with -l, --name-only or --object-only. The format string is single-quoted so the shell leaves its percent expressions alone.

6. Handle names and object IDs carefully

Use -l for blob sizes:

$ git ls-tree -l HEAD README.md
100644 blob 4a58007052a65fbc2fc3f910f2855f45a4058e74       6    README.md

The size is in bytes and only shown for blobs. Trees and commits get a dash instead, and it is never the size of a directory on disk.

For a shorter, human-facing ID, use --abbrev=12:

$ git ls-tree --abbrev=12 HEAD README.md
100644 blob 4a58007052a6    README.md

The abbreviation is at least the requested length and stays unique within the object set Git uses for the display. Do not treat an abbreviated ID as durable interchange data when a full object name is practical instead.

Git quotes unusual pathnames according to core.quotePath by default. For machine processing add -z: paths come out verbatim and records end in NUL bytes. A line-oriented tool that chokes on NUL separators is not a safe consumer for arbitrary Git names.

7. Verify the result and diagnose failures

Compare a pathname-only listing against a known path before building a longer pipeline:

$ git ls-tree -r --name-only HEAD src | grep -F -- 'src/main.txt'
src/main.txt

No output from grep means that exact path was not in the selected tree, not that the command failed. To inspect a different snapshot, swap HEAD for a commit or tag and repeat the check.

If Git says an object or path does not exist, check the revision independently with git rev-parse --verify. If a path works from the repository root but not from a subdirectory, add --full-tree or adjust the path to match where you are. If a script gets surprising filenames, switch to -z and read it with a NUL-aware reader.

There is no undo step for the normal commands here, since they only inspect existing objects. If you redirect output to a file, pick a fresh destination or check it first: > truncates an existing file before git ls-tree even runs.

Done means

  • Tree identified: you picked a real tree-ish and confirmed the installed Git version.
  • Listing modes clear: you can tell top-level, directory-only and recursive listings apart.
  • Path scope understood: you know paths depend on the current directory unless --full-tree is used.
  • Script output chosen: your script picks a single-field mode or an explicit --format, not both.
  • NUL-safe: you use -z whenever arbitrary pathnames are possible.
  • Nothing touched: no branch, index, working-tree file or repository object was changed.