Олег Марков
Просмотр дерева файлов в Git с помощью git ls-tree
Введение
Команда git ls-tree относится к "низкоуровневым" инструментам Git. Обычно вы работаете с более привычными командами вроде git status, git log, git diff. Но когда нужно точно понять, что именно лежит внутри конкретного коммита, тега или любой другой ссылки в Git, git ls-tree становится очень полезной.
Смотрите, я покажу вам, как с помощью git ls-tree можно:
- просматривать структуру файлов и директорий в любом коммите;
- видеть тип объектов (blob, tree, submodule и так далее);
- получать хеши файлов и деревьев, чтобы затем использовать их в других командах;
- анализировать состояние репозитория без извлечения файлов в рабочую директорию.
Команда сама по себе не изменяет репозиторий. Она только "подсвечивает" его внутреннюю структуру. Это удобно, когда вы хотите понять, что происходит "под капотом" Git, не рискуя что-то испортить.
Теперь давайте разберемся, как устроено дерево файлов в Git и что именно показывает git ls-tree.
Что такое дерево файлов в Git
Объекты в Git: blob, tree, commit
Чтобы комфортно пользоваться git ls-tree, полезно немного знать о внутреннем устройстве Git.
Внутри Git есть несколько ключевых типов объектов:
- blob — содержимое файла (без имени и пути);
- tree — каталог, который хранит список файлов и подкаталогов;
- commit — объект коммита, который указывает на корневое дерево (tree) и содержит метаданные коммита.
Если упростить, один коммит — это:
- указатель на корневой объект tree;
- сведения об авторе, времени и сообщении;
- ссылки на родительские коммиты.
А корневой tree уже содержит:
- список записей вида:
- права доступа (mode);
- тип объекта (blob, tree, commit);
- хеш объекта;
- имя файла или каталога.
Команда git ls-tree как раз показывает содержимое tree-объекта в удобном текстовом виде.
Где здесь git ls-tree
Когда вы вызываете git ls-tree, вы говорите Git:
Покажи, какие объекты (файлы и папки) лежат в дереве, на которое указывает вот эта ссылка (коммит, ветка, тег или конкретный tree-объект).
Например:
- git ls-tree HEAD — покажет дерево файлов текущего коммита;
- git ls-tree main — дерево файлов на вершине ветки main;
- git ls-tree 1a2b3c4d — содержимое дерева, связанного с коммитом с этим хешем.
Теперь давайте посмотрим, как выглядит базовый вывод и как его читать.
Базовый синтаксис git ls-tree
Общий вид команды
Синтаксис команды:
git ls-tree [опции] <tree-ish> [путь]
Где:
- tree-ish — это любой указатель на дерево:
- имя ветки (main, develop и так далее);
- хеш коммита;
- тег;
- хеш tree-объекта;
- путь (необязательный) — позволяет ограничить вывод конкретным каталогом или файлом внутри дерева.
Пример самого простого использования:
git ls-tree HEAD
Здесь Git возьмет коммит HEAD, найдёт его корневое дерево и покажет, что в нем лежит.
Как читать вывод git ls-tree
Давайте разберем пример. Допустим, у вас есть репозиторий, и вы выполняете:
git ls-tree HEAD
Вы можете увидеть что-то вроде:
100644 blob 3b18e512e5f3d0c0c7c5a7b68e2c2fdee4d3f0a0 README.md 100644 blob a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3 main.go 040000 tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65 internal 160000 commit 9f3c0295c4f2484054a39f3c4bb72e9f142d65dd vendor
Давайте посмотрим, что означает каждая колонка:
- 100644, 040000, 160000 — режим (mode), по сути Unix-права и тип файла.
- 100644 — обычный файл;
- 100755 — исполняемый файл;
- 040000 — каталог (tree);
- 120000 — символьная ссылка;
- 160000 — gitlink (подмодуль).
- blob, tree, commit — тип объекта Git:
- blob — содержимое файла;
- tree — каталог;
- commit — используется для подмодулей, когда сам каталог ссылается на конкретный коммит в другом репозитории.
- Хеш — SHA-1 или SHA-256 (в зависимости от настройки репозитория) объекта.
- Имя — имя файла или каталога в этом дереве.
Обратите внимание, что git ls-tree показывает логическую структуру репозитория, а не состояние вашей рабочей директории. Он не смотрит на незакоммиченные изменения.
Ключевые опции git ls-tree
Теперь давайте подробно разберем самые полезные опции git ls-tree и посмотрим на примерах, что они делают.
Опция -r — рекурсивный обход дерева
По умолчанию git ls-tree показывает только содержимое текущего дерева — без рекурсивного захода в подкаталоги.
Если вы хотите увидеть все файлы во всех подпапках, используйте флаг -r:
git ls-tree -r HEAD
Вывод получится примерно таким:
100644 blob 3b18e512e5f3d0c0c7c5a7b68e2c2fdee4d3f0a0 README.md 100644 blob a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3 main.go 100644 blob 2d1bdc2849cf0ee2d7c53295c79fba753f411c2b internal/service/user.go 100644 blob 1f98f3c39ef6a86bff5c3ab9aa91a366b24a7b0a internal/storage/db.go
Здесь вы уже видите полный путь до файла внутри репозитория.
Чтобы было проще сравнить:
- без -r — вы видите только список элементов верхнего уровня;
- с -r — вы видите полное "дерево файлов" с путями.
Опция -t — показывать деревья (каталоги) в рекурсивном режиме
Если вы используете -r, Git по умолчанию показывает только файлы (blob). Каталоги (tree) как отдельные записи в вывод не попадают, они просто раскрываются.
Если вам нужно видеть и каталоги тоже, добавьте -t:
git ls-tree -r -t HEAD
Теперь в выводе будут и файлы, и каталоги:
040000 tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65 internal 100644 blob 2d1bdc2849cf0ee2d7c53295c79fba753f411c2b internal/service/user.go 100644 blob 1f98f3c39ef6a86bff5c3ab9aa91a366b24a7b0a internal/storage/db.go
Это бывает полезно, если вы хотите анализировать структуру каталогов, а не только список файлов.
Опция -d — показывать только каталоги, без рекурсии внутрь
Если вы хотите увидеть только каталоги верхнего уровня (без файлов и без рекурсивного вывода), используйте -d. Часто эту опцию комбинируют с -r, чтобы показать все каталоги на всех уровнях без файлов:
git ls-tree -r -d HEAD
В результате вы получите список только tree-объектов:
040000 tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65 internal 040000 tree 1b4a85f8f4d5d57e74f4b5f558dd6e4c514ebd3c internal/service 040000 tree 7cf9b146ca0a0e0b18d9e8af5aae41f1acec5d48 internal/storage
Смотрите, так удобно быстро оценить структуру каталогов в коммите, не разгружая рабочую директорию.
Опция -l — вывод размера файлов
Если вы хотите одновременно увидеть и хеши, и размеры файлов, добавьте -l:
git ls-tree -r -l HEAD
Пример вывода:
100644 blob 3b18e512e5f3d0c0c7c5a7b68e2c2fdee4d3f0a0 1234 README.md 100644 blob a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3 20480 main.go
Здесь:
- 1234 и 20480 — размер содержимого blob-объекта в байтах.
Каталоги и подмодули размера не имеют, поэтому у них это поле пустое или не выводится.
Опция -z и работа с именами файлов с пробелами
Если в репозитории есть файлы с пробелами или нестандартными символами в именах, вывод git ls-tree может быть неудобно обрабатывать скриптами. Для таких случаев есть опция -z.
Она меняет формат вывода:
- вместо перевода строки используется нулевой байт как разделитель записей;
- имена файлов могут содержать буквально любые символы, включая перевод строки.
Пример:
git ls-tree -r -z HEAD
Обычно эту опцию используют в связке с другими утилитами, например xargs -0 или while IFS= read -r -d ''.
Давайте разберем мини-пример на shell, чтобы было понятнее.
Представьте, вам нужно вывести только имена всех файлов в коммите, даже если в названиях есть пробелы или спецсимволы:
git ls-tree -r -z --name-only HEAD | \ while IFS= read -r -d '' path; do
# Здесь мы обрабатываем каждый путь
echo "Файл из коммита HEAD: $path"done
Комментарии в этом примере:
- флаг -z в git ls-tree делает разделителем записей нулевой байт;
- опция -d '' в read говорит, что разделитель — нулевой байт;
- так вы надежно обрабатываете даже "сложные" имена файлов.
Опция --name-only и --name-status
Если вам не нужны режимы, типы и хеши объектов, а нужны только пути, используйте:
- --name-only — показывает только имена файлов;
- --name-status — для git ls-tree обычно не применяется, в отличие от git diff-tree. В контексте ls-tree основной полезный вариант — именно --name-only.
Например:
git ls-tree -r --name-only HEAD
Вывод будет упрощенным:
README.md main.go internal/service/user.go internal/storage/db.go
Это удобно, когда вы хотите быстро получить список файлов в коммите, например, чтобы использовать его в другом скрипте.
Просмотр дерева разных ссылок: ветки, коммиты, теги
Сравнение дерева в разных коммитах
Частая задача — понять, какие файлы были в каком-то старом коммите, не делая git checkout. Смотрите, я покажу вам простой пример.
Допустим, у вас есть текущий HEAD и коммит с хешем abc1234. Вы хотите сравнить их содержимое.
Сначала посмотрите дерево HEAD:
git ls-tree -r --name-only HEAD
Затем дерево старого коммита:
git ls-tree -r --name-only abc1234
Чтобы автоматически найти различия, вы можете использовать обычные Unix-утилиты:
git ls-tree -r --name-only abc1234 > /tmp/filesold git ls-tree -r --name-only HEAD > /tmp/filesnew
diff -u /tmp/filesold /tmp/filesnew
Комментарии:
- git ls-tree в обоих случаях показывает список файлов, зафиксированных в соответствующем коммите;
- diff показывает, какие пути появились, пропали или изменились.
Если нужно сравнить именно содержимое (а не только список файлов), обычно используют git diff, но git ls-tree полезен, когда вас интересует именно структура дерева.
Просмотр дерева по тегу
Если у вас в репозитории есть теги (например, v1.0.0), вы можете посмотреть структуру файлов в момент релиза:
git ls-tree -r --name-only v1.0.0
Это особенно удобно в больших проектах, где нужно увидеть, какие файлы входили в сборку определенной версии.
Просмотр дерева по хешу tree-объекта
Хотя чаще всего вы указываете коммит (commit), git ls-tree может работать и напрямую с tree-объектами.
Например, вы взяли хеш дерева из вывода какой-то низкоуровневой команды и хотите посмотреть его содержимое:
git ls-tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65
Git покажет дерево, связанное непосредственно с этим tree-объектом, без привязки к коммиту. Это уже более "внутренний" сценарий, но иногда полезно для отладки или анализа.
Фильтрация по пути и работа с поддиректориями
Просмотр только части дерева
Допустим, вам нужно посмотреть не все дерево, а только содержимое каталога internal в HEAD. Вместо того чтобы выводить все и потом фильтровать, вы можете сразу указать путь:
git ls-tree HEAD internal
Вывод покажет только содержимое catalog internal на верхнем уровне:
040000 tree 1b4a85f8f4d5d57e74f4b5f558dd6e4c514ebd3c service 040000 tree 7cf9b146ca0a0e0b18d9e8af5aae41f1acec5d48 storage
Если добавить -r, вы получите все файлы в этой поддиректории:
git ls-tree -r HEAD internal
Это удобно, когда внутри коммита крупный проект, но вы хотите сфокусироваться на конкретном модуле.
Просмотр конкретного файла в дереве
Если вы укажете имя файла, git ls-tree покажет только запись, связанную с этим файлом:
git ls-tree HEAD main.go
Пример вывода:
100644 blob a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3 main.go
Иногда это полезно, если вы хотите быстро получить хеш blob-объекта для файла в конкретном коммите, чтобы потом использовать его, например, в git cat-file.
Работа с подмодулями и gitlink (mode 160000)
Как ls-tree показывает подмодули
Когда в репозитории используется подмодуль, Git хранит его как специальный тип записи в дереве — gitlink. В выводе ls-tree это выглядит так:
160000 commit 9f3c0295c4f2484054a39f3c4bb72e9f142d65dd vendor
Что здесь важно:
- режим 160000 — это именно подмодуль;
- тип commit — ссылка на конкретный коммит в другом репозитории;
- имя vendor — путь к каталогу подмодуля.
Таким образом, git ls-tree позволяет увидеть, на какой именно коммит в подмодуле указывает данный коммит "основного" репозитория.
Как использовать эту информацию на практике
Например, вы хотите узнать, какой коммит подмодуля был подключен в релизе v1.0.0. Вы можете выполнить:
git ls-tree v1.0.0 vendor
И вы увидите хеш коммита подмодуля, который был зафиксирован на тот момент. После этого можно перейти в каталог подмодуля и проверить, какой это был коммит:
cd vendor git show 9f3c0295c4f2484054a39f3c4bb72e9f142d65dd
Комментарии:
- так вы связываете состояние основного проекта и состояние подмодуля;
- это помогает разбираться с воспроизводимостью сборок и отладкой старых версий.
Использование git ls-tree вместе с другими командами
Связка git ls-tree и git cat-file
Часто git ls-tree используют в паре с git cat-file. Первая команда показывает структуру дерева и хеши объектов, вторая — позволяет посмотреть содержимое или тип объекта по его хешу.
Допустим, вы хотите узнать содержимое файла main.go в коммите HEAD, не делая checkout.
Сначала берете хеш blob с помощью ls-tree:
git ls-tree HEAD main.go
Вывод:
100644 blob a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3 main.go
Теперь вы используете git cat-file:
git cat-file -p a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3
Git выведет содержимое файла main.go, зафиксированного в этом коммите.
Комментарии:
- это удобный способ "заглянуть" внутрь файлов в старых коммитах без переключения веток;
- так часто делают в скриптах и инструментах, которые анализируют историю проекта.
Выборка файлов определенного типа
Давайте посмотрим пример, как с помощью git ls-tree можно найти все Go-файлы в коммите и посчитать их количество.
Предположим, вы хотите узнать, сколько файлов .go было в версии v1.0.0:
git ls-tree -r --name-only v1.0.0 | \ grep '.go$' | \ wc -l
Комментарии:
- git ls-tree -r --name-only v1.0.0 — получаем список всех файлов;
- grep '.go$' — фильтруем по расширению .go;
- wc -l — считаем количество строк, то есть файлов.
Такой подход работает для любых типов файлов и может быть встроен в скрипты метрик или анализа.
Поиск "потерянных" файлов по хешу
Иногда у вас на руках есть только хеш blob-объекта, и вы хотите понять, в каких коммитах и под какими путями он встречается. На 100 процентов эту задачу решают другие инструменты вроде git log --all --find-object, но git ls-tree может быть частью такого поиска.
Простейший пример в виде скрипта (для понимания идеи):
git rev-list --all | \ while read commit; do
# Для каждого коммита смотрим дерево рекурсивно
git ls-tree -r "$commit" | \
grep 'a9f3d98f9a0e89b87d94d10da3b9580a4151d1e3' && \
echo "Найдено в коммите $commit"done
Комментарии:
- git rev-list --all — перечисляет все коммиты;
- git ls-tree -r "$commit" — показывает дерево каждого коммита;
- grep — ищет по хешу нужного blob.
На больших репозиториях так делать не стоит (будет медленно), но идея хорошо демонстрирует, как ls-tree может участвовать в сложных сценариях анализа.
Сравнение git ls-tree с другими командами Git
git ls-files vs git ls-tree
Эти две команды часто путают, поэтому давайте сравним.
git ls-files:
- показывает файлы, которые отслеживаются в текущем индексe (staging area);
- отражает состояние "под готовку коммита" плюс некоторые особенности рабочего дерева;
- не привязан к конкретному коммиту, а к текущему состоянию репозитория.
git ls-tree:
- показывает структуру файлов в конкретном tree-объекте (коммиты, теги и так далее);
- работает с историей, а не с незакоммиченными изменениями;
- не зависит от текущего состояния рабочей директории.
Если упростить:
- git ls-files — "что сейчас отслеживает Git, включая подготовленные изменения";
- git ls-tree — "что зафиксировано в указанном коммите или дереве".
git diff-tree и git ls-tree
git diff-tree тоже работает с деревьями, но уже в контексте сравнения.
- git ls-tree показывает само дерево (список файлов/каталогов) в конкретном состоянии;
- git diff-tree показывает, чем одно дерево отличается от другого.
Например:
- git ls-tree -r HEAD — просто список файлов в HEAD;
- git diff-tree --name-status HEAD^ HEAD — список файлов, которые изменились между предыдущим и текущим коммитом.
Эти команды дополняют друг друга, когда нужно и посмотреть структуру, и сравнить версии.
Практические сценарии использования git ls-tree
Быстрая проверка состава релиза
Представьте, вы готовите релиз, который помечен тегом v2.0.0. Вам нужно убедиться, что в релиз вошли только нужные файлы, без временных каталогов и лишних артефактов.
Давайте разберемся на примере.
Сначала смотрите полное дерево:
git ls-tree -r --name-only v2.0.0
Затем фильтруете по нежелательным шаблонам, например:
git ls-tree -r --name-only v2.0.0 | \ grep -E '(^.idea/|.DS_Store$|/tmp/)'
Если вывод пустой, значит, в релизе нет "мусорных" файлов. Если что-то вылезло — можно решить, нужно ли это поправить и пересобрать релиз.
Анализ старой структуры проекта
Бывает, что проект сильно менялся, папки переезжали, а вы хотите понять, как он был устроен год назад. Вместо того чтобы переключаться на старые ветки, можно посмотреть дерево прямо так:
git ls-tree -r <старый-коммит>
Например:
git log --since="1 year ago" --max-count=1 --format="%H"
Предположим, команда выше вернула хеш 1234567. Теперь смотрите дерево того коммита:
git ls-tree -r 1234567
Так вы можете проследить эволюцию структуры папок во времени, не трогая рабочую директорию.
Генерация артефактов без checkout (низкоуровневый способ)
Это уже более продвинутый сценарий, но он хорошо показывает, насколько git ls-tree "низкоуровневый".
Представьте, вы хотите собрать архив с файлами из коммита, не переключаясь на него:
- получаете список файлов через git ls-tree;
- с помощью git cat-file читаете содержимое каждого файла;
- формируете собственный архив.
В реальной жизни проще использовать git archive, но понимание того, как ls-tree и cat-file могут это сделать, помогает лучше понимать внутреннюю архитектуру Git.
Условный псевдокод на shell (упрощенно, не для продакшена):
commit=HEAD
Получаем список файлов с хешами
git ls-tree -r "$commit" | while read mode type hash path; do
# Создаем необходимые каталогиmkdir -p "out/$(dirname "$path")"
# Извлекаем содержимое файла по хешуgit cat-file -p "$hash" > "out/$path"
# Здесь каждый файл в out/ соответствует файлу из коммитаdone
Комментарии:
- ls-tree здесь используется как "карта" дерева репозитория;
- cat-file — как способ "вытащить" содержимое по хешу;
- такой подход применяют в собственных CI-инструментах или когда нужно особое поведение сборки.
Заключение
Команда git ls-tree — это инструмент для просмотра дерева объектов в Git. Она показывает, какие файлы и каталоги входят в конкретный коммит, ветку, тег или tree-объект, и в каком виде они там хранятся.
Вы увидели, что ls-tree:
- умеет работать с любыми ссылками на дерево (ветки, коммиты, теги, хеши tree);
- показывает тип объекта (blob, tree, commit) и режим (включая подмодули);
- с помощью флагов -r, -d, -t, -l и --name-only позволяет удобно настраивать формат вывода;
- хорошо комбинируется с другими командами — git cat-file, git diff-tree, git rev-list, фильтрацией через grep и так далее.
Эта команда полезна не только для "гиковского" изучения внутренностей Git, но и для практических задач: проверки состава релизов, анализа старой структуры проекта, работы с подмодулями, генерации отчетов и служебных скриптов.
Если вы уверенно чувствуете себя с git log, git diff и git status, переход к git ls-tree помогает сделать следующий шаг — лучше понимать, как Git действительно хранит файлы и каталоги внутри себя.
Частозадаваемые технические вопросы
Как с помощью git ls-tree понять, какие файлы изменились между двумя коммитами
git ls-tree сам по себе не сравнивает деревья, он только показывает содержимое одного дерева. Для сравнения используйте git diff-tree:
список измененных файлов:
git diff-tree --name-status -r OLDCOMMIT NEWCOMMIT
если нужен только список путей:
git diff-tree --name-only -r OLDCOMMIT NEWCOMMIT
ls-tree здесь можно использовать дополнительно, чтобы посмотреть детали по конкретному коммиту до или после diff.
Как вывести git ls-tree в стабильном отсортированном виде
По умолчанию git ls-tree сортирует записи по имени. Если вы хотите убедиться в этом явно, используйте:
простой вывод:
git ls-tree -r HEAD
если нужен "сырой" вывод для скриптов, который легко парсить, добавьте --full-tree и --name-only по необходимости:
git ls-tree -r --full-tree HEAD
Дополнительной опции "sort" у ls-tree нет, поэтому, если нужно сортировать по размеру или типу, делайте это внешними инструментами, например:
git ls-tree -r -l HEAD | sort -k4
Как увидеть git ls-tree относительно корня, если я нахожусь в подкаталоге
По умолчанию, если вы запускаете git ls-tree из подкаталога, Git показывает пути относительно корня репозитория, но иногда поведение может казаться запутанным. Чтобы явно получить пути от корня, используйте опцию --full-tree:
git ls-tree -r --full-tree HEAD
Так Git не будет "сдвигать" пути относительно текущей директории.
Можно ли использовать git ls-tree для некоммитнутых изменений
Нет, ls-tree работает только с объектами в хранилище Git — коммитами и tree-объектами. Некоммитнутые изменения он не видит. Чтобы посмотреть текущее состояние с учетом индекса и рабочего каталога, используйте:
- git status — краткий обзор;
- git diff — разницу относительно коммита;
- git ls-files — список отслеживаемых файлов.
Если нужно совместить ls-tree и некоммитнутые изменения, обычно смотрят ls-tree HEAD и затем анализируют diff.
Как с помощью git ls-tree понять, какой корневой tree у коммита
Обычно git ls-tree сам находит корневое дерево по коммиту, но если вы хотите увидеть хеш именно корневого tree-объекта, выполните:
git cat-file -p COMMIT_HASH | head -n 1
Вы увидите строку вида:
tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65
Затем можете вызвать:
git ls-tree 5c7b1b33aaf27093ab1c5f3ba46ef2a3d1a57f65
Так вы явно работаете с tree-объектом, а не с коммитом.
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев