lsw: одна команда, чтобы увидеть все свои git worktree

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Если вы работаете в стиле «одна задача — свой worktree», у вас наверняка скопилась папка, где рядом лежат полдюжины чекаутов одного проекта. Я в какой-то момент устал бегать по ним с git status и написал lsw — это «ls» для worktree. Одна команда показывает все репозитории и worktree вокруг: ветку каждого, есть ли незакоммиченные правки, насколько ветка ушла от origin. Read-only, без зависимостей, один бинарник.

Откуда это взялось

Напомню на всякий случай, для тех кто не пользуется: git worktree add ../webapp-1042 feature/TASK-1042 создаёт второй рабочий каталог того же репозитория. Две ветки чекнуты одновременно, никаких стэшей и git checkout-каруселей: в одной папке чините прод, в другой спокойно пишете фичу. Штука недооценённая, я почти всю разработку так и веду — каждая задача в своём каталоге. Через месяц рядом с проектом скапливается примерно такое:

webapp/            ← main, главный клон
webapp-1042/       ← фича TASK-1042, в ней сейчас работаю
webapp-hotfix/     ← а тут висят незакоммиченные правки
webapp-review/     ← ревью чужого PR
docs/

И каждый раз, возвращаясь к проекту, я прохожу один и тот же квест: в какой папке я вчера остановился, где висят незакоммиченные правки, какая ветка уехала вперёд от origin, а какая отстала, и вообще жива ли ещё та remote-ветка. Руками это cd по всем каталогам плюс git status в каждом. С двумя терпимо, с пятью — начинает бесить. git worktree list помогает частично: он работает только внутри одного репозитория и ничего не знает ни про dirty-состояние, ни про ahead/behind.

Готовой лёгкой утилиты, которая отвечала бы на всё это одной командой по всей папке проектов, мне не попалось. Так и появился lsw.

Что показывает

Запускаю в папке с проектами:

lsw — древовидный вывод
lsw — древовидный вывод

Внутри репозитория lsw показывает его worktree, внутри обычной папки — сканирует репозитории на уровень вглубь (-2 и -3 — глубже). Для каждого worktree — ветка, грязность и дистанция до upstream.

Токен

Значение

./repo · host/user/repo

заголовок группы: путь и origin в компактном виде

* (зелёный)

worktree, в котором вы сейчас находитесь

N dirty

незакоммиченные изменения (жёлтым)

↑N

ветка впереди upstream на N коммитов (циан)

↓N

позади upstream на N (красный)

no upstream

у ветки нет remote tracking

upstream gone

remote-ветка удалена

bare

bare-репозиторий

Флаг -b переключает на карточки, там добавляются remote tracking branch и последний коммит:

lsw -b — блочный вывод
lsw -b — блочный вывод

А -bp расписывает грязное состояние пофайлово, с +добавлено/−удалено:

lsw -bp — пофайловая детализация
lsw -bp — пофайловая детализация

Чего в lsw намеренно нет

В нём нет нескольких вещей. Состояния, конфигов, демона — lsw только читает, в ваши репозитории не пишет никогда. Сторонних крейтов — только Rust std и тот самый git, который у вас уже стоит; ни libgit2, ни OpenSSL, аудитить нечего, один статический бинарник на ~1 МБ. И управления worktree — создание, удаление, prune я оставил git worktree, он с этим и так справляется, дублировать смысла нет.

Весь ввод-вывод сводится к одному git worktree list на репозиторий и одному git status на worktree, дальше только разбор текста. Я писал это под omarchy и старался держать в духе: маленькие острые тулзы, которые стыкуются друг с другом. Цветной вывод включается только в TTY, в пайпе текст чистый — так что lsw | grep dirty и lsw | fzf работают как надо. NO_COLOR и NO_ICONS уважаются, а без Nerd Font иконки деградируют в ASCII (или можно --no-icons принудительно).

Под капотом

Чистый Rust: около двух тысяч строк вместе с тестами, ноль сторонних крейтов, 53 теста. Собирается на Rust 1.70+ плюс системный git.

Скан каталогов

Обход идёт по уровням (по умолчанию один, -2/-3 — глубже); скрытые каталоги, node_modules и target не трогаем. Узлы различаются по .git: каталог с .git/ внутри — репозиторий, .git-файл — linked worktree (именно так git оформляет связанные чекауты: файл с gitdir:-ссылкой). На каждый найденный репозиторий — один git worktree list, который сразу отдаёт все его worktree, включая bare.

Дальше начинается интересное. Worktree, вложенные внутрь каталога репозитория (классическая раскладка repo/{main,wt1,wt2}), попадают под его заголовок и не дублируются. Worktree-сиблинги, лежащие рядом с репозиторием, получают собственные группы.

Как я поймал баг

Разделить эти два случая с первого раза не получилось. До v0.1.3 дедуп в скане выкидывал worktree, чей «настоящий» корень уже встречался в списке. Для вложенных worktree логика «уже показан этим репозиторием» работала, но сиблинг, встретившийся после своего репозитория по алфавиту, тоже выкидывался — а рендерил его только репозиторий, в чьё поддерево сиблинг не попадал. В итоге две папки просто исчезали из выдачи. Причём результат зависел от порядка обхода: another_tasks (a < c) выживал, а supports и support_tasks (s > c) — нет.

Починилось одной строкой условия: дедуп применяем только к worktree, вложенным внутрь каталога уже показанного репозитория. Писал по TDD — сначала два падающих теста (репро реальной раскладки и проверка независимости от сортировки), потом фикс, потом все 53 зелёные. Докстринги врут даже в коде на несколько сотен строк, я это ещё раз проверил.

Состояние и деградации

Dirty-счётчик и ↑/↓ берутся из одного git status на worktree; -bp разворачивает полный список файлов. Stale-worktree, у которых .git указывает на исчезнувший репозиторий, пропускаются молча, а --debug покажет на stderr, что и почему пропущено. Когда ничего не найдено, lsw не молчит, а печатает короткую заметку: где искал, на какой глубине, сколько каталогов было нечитаемо — и выходит с кодом 0.

Установка

# инсталлятор: prebuilt-бинарник последнего релиза, x86_64-linux
curl -fsSL https://raw.githubusercontent.com/apilot/lsw/main/install.sh | sh
# или сборка из исходников (любая платформа с Rust и git)
cargo install --git https://github.com/apilot/lsw

Скрипт проверяет sha256 скачанного бинарника и кладёт его в ~/.local/bin (перезадаётся через LSW_PREFIX). Работает на любой ОС, где в PATH есть git.

Вместо заключения

Утилита маленькая и специально ограниченная: она не управляет worktree, а просто делает видимым то, что git уже знает. Мне она заменила утренний обход папок одной командой — может, и вам пригодится.

GitHub: apilot/lsw — issue и PR жду, особенно раскладки worktree-хозяйства, которые скан обрабатывает неправильно: таких layout’ов столько, сколько у людей привычек. AUR-пакет уже готов, ждал открытия регистрации (её прикрывали из-за наплыва ботов), уйдёт в репозиторий как только откроют.

Источник: https://habr.com/ru/articles/1096168/


Интересные статьи

Интересные статьи

Привет друг. Меня зовут Разработчик. Вид веб-разработка, подвид битриксоидус. Это не тот кем я хотел когда-то стать, но десять лет сделали это свершившимся фактом. Я устал быть им. Но перед тем как за...
При написании высококачественного программного обеспечения не обойтись без этапа формальной верификации. Несмотря на то, что наша жизнь уже была в некоторой степени упрощена, благодаря таким помощника...
Добавляем функционал быстрых ответов на любом тарифе Битрикс24Как добавить одну из самых полезных функций для чатов открытых линий для любого коммерческого тарифа? Все очень просто, нужно лишь..... ...
Тема Китая и производства электроники затрагивалась на Хабре не раз и не два. КНР пытается решить проблему санкций и развернуть собственную инфраструктуру производства современных чипов и электро...
Всем привет. Когда я искал информацию о журналировании (аудите событий) в Bitrix, на Хабре не было ни чего, в остальном рунете кое что было, но кто же там найдёт? Для пополнения базы знаний...