Marko Ivankovic выпустил OmniDiff, быстрый diff для git на Rust

Marko Ivankovic выпустил OmniDiff, быстрый diff для git на Rust

Marko Ivankovic опубликовал на GitHub OmniDiff, программу для сравнения файлов, написанную на Rust. Автор описывает её как быструю, надёжную и точную, учитывающую тип содержимого. В README заявлены два главных показателя: инструмент справляется с 99,95% изменений кода из тестового набора, а 98,8% изменений разбираются алгоритмами, специфичными для типа содержимого.

Как это работает. Язык определяется по расширению файла. Для 25 языков используется грамматика tree-sitter, и сравнение идёт по структуре кода. Файлы с неизвестным расширением сравниваются как обычный текст построчно, так что ничего не отклоняется. Для шести форматов (Bazel, Dart, Emacs Lisp, Markdown, Protocol Buffers, SQL) грамматики не подключены, и они тоже сравниваются как текст. Не текстовые файлы распознаются по байтам, а не по имени, и сравниваются по содержимому: архивы, шрифты, PDF, картинки (в том числе анимированные), файлы OpenDocument, скомпилированные каталоги переводов. Открываются только изменившиеся элементы: у шрифта с двумя правками покажут два глифа. Каждое изменение получает оценку заметности для читателя: невидимое (отличаются лишь байты), неразличимое, артефакты сжатия или передискретизации, правка, перерисовка или замена. Любой другой бинарный файл получает однострочное сообщение «Binary file … differs».

Интерфейс и интеграции. Есть полноэкранный терминальный просмотрщик (TUI) с 24-битным цветом, если терминал его объявляет, и с 256 цветами в остальных случаях. Картинки он показывает средствами терминала (протоколы kitty, sixel, iTerm2) или полублоками Unicode. Режим --headless (синоним --batch) печатает diff простым текстом и включается автоматически, если вывод идёт не в терминал, например в less или файл. Режим --mode json печатает diff одним JSON-объектом со схемой в репозитории. По умолчанию показываются 3 строки контекста вокруг изменения (меняется ключом --context N), а перед блоком изменений добавляется ближайшая объемлющая функция, класс или структура. Код возврата: 0 при успехе, 2 при ошибке, а 1 при различиях, только с флагом --exit-code, чтобы системы контроля версий не считали отличие файлов сбоем инструмента.

Инструмент подключается как difftool для git (мастер настройки: omnidiff git configure) и через GIT_EXTERNAL_DIFF для git diff и git log -p; там вывод всегда текстовый, интерактивный просмотрщик открывает только git difftool. Для jj есть отдельный мастер (omnidiff jj configure), при этом необходим режим вызова file-by-file. Расширения: для VS Code и Neovim (omnidiff-vscode, omnidiff.nvim; встроенный diff-просмотр VS Code пока нельзя заменить расширением, нужный API реализован в основной ветке, но не выпущен). Есть и браузерный инструмент ревью codereview, но он версии v0.0.0, и автор советует использовать его на свой риск.

Установка: cargo install --locked omnidiff (нужны компилятор C и rustc 1.92 или новее; первая сборка занимает несколько минут из-за компиляции грамматик и профиля lto = "fat"), готовые бинарники для Linux, macOS (Intel и Apple Silicon) и Windows в релизах GitHub, brew, подписанный apt-репозиторий с пакетами .deb для amd64 и arm64, nix run, рецепты для Arch и Gentoo.

Скорость. Цели автора: медианный diff не дольше 100 мс и 99-й процентиль не дольше 1000 мс; README утверждает, что обе выполнены. На 2302 тестовых примерах (измерение от 31 октября 2026 года на ноутбуке с Intel Core i7-1370P и 32 ГБ ОЗУ): медиана 4,2 мс, p90 35,1 мс, p99 177 мс, самый медленный случай 482 мс. 100 мс, это 97,6-й процентиль: дольше идут 55 примеров, но ни один не дольше секунды. Серверу с Intel Xeon E3-1275 v5 на тот же пример нужно примерно в 2,5 раза больше времени. Для прочего содержимого картина хуже: архивы не укладываются в цель по 99-му процентилю (в самых тяжёлых тысячи изменённых элементов), PDF обе цели выполняют, но лишь 57% из них считаются быстрее 100 мс, поскольку отрисовываются все изменённые страницы.

Как получена цифра 99,95%. Тестовый набор, история коммитов примерно 7500 открытых git-репозиториев (список взят из дистрибутива Gentoo Linux). Взяты все изменённые файлы с кодом из последних 50 коммитов каждого репозитория: 442 530 читаемых пар «до/после» на 25 языках, сравнение без ограничения размера, с лимитом 120 секунд и 6 ГБ памяти на процесс. Успешно завершились 442 322 пары (99,95%); 96 пар вышли за лимит времени и 112 за лимит памяти. Цель автора, обработать 100% всех коммитов.

Лицензия. Программа распространяется по GNU AGPL версии 3, авторские права принадлежат Marko Ivankovic (2026). Для организаций предлагается коммерческая лицензия по ежемесячной подписке через GitHub Sponsors: она покрывает внутреннее использование без обязательства раскрывать исходный код по AGPL.

Ключевые факты

  • OmniDiff, открытый diff-инструмент на Rust: TUI-просмотрщик, безголовый режим (--headless), вывод в JSON, интеграция с git (difftool и GIT_EXTERNAL_DIFF) и jj.
  • Для 25 языков код сравнивается по структуре через tree-sitter; архивы, шрифты, PDF, картинки и OpenDocument сравниваются по содержимому, неизвестные расширения, построчно как текст.
  • Скорость по данным README: на 2302 примерах на ноутбуке автора (Core i7-1370P) медиана 4,2 мс, p99 177 мс, самый медленный случай 482 мс; на сервере с Xeon E3-1275 v5 примерно в 2,5 раза медленнее.
  • Цифра 99,95%, доля пар, завершившихся в лимите 120 с и 6 ГБ: 442 322 из 442 530 пар из примерно 7500 репозиториев; 96 пар вышли за лимит времени, 112, за лимит памяти.
  • Лицензия AGPL-3.0; коммерческая лицензия без обязательств AGPL, по ежемесячной подписке через GitHub Sponsors.

Почему это важно

Сравнение файлов, ежедневная операция в разработке, а привычный построчный diff плохо различает структуру кода и вовсе не умеет показывать содержательные изменения в бинарных форматах. OmniDiff пытается закрыть оба пробела: разбирает код 25 языков по грамматикам tree-sitter и отдельно описывает изменения в архивах, шрифтах, PDF и картинках. Заявленная скорость (медиана 4,2 мс на ноутбуке автора) подаётся как достаточная, чтобы инструмент не мешал в повседневной работе, но эти цифры взяты из README и измерены на одной машине.

Кому это важно

Прежде всего разработчикам, которые много читают дифы в терминале, используют git или jj и хотят видеть изменения в контексте функций и классов. Также тем, кто сравнивает не только текст: например, версии шрифтов, документов OpenDocument или картинок. Команды, которым нужен вывод для скриптов и CI, могут воспользоваться безголовым режимом и JSON-выводом. Организациям стоит заранее учесть условия лицензии AGPL.

Как это применить

Установка: cargo install --locked omnidiff (нужны компилятор C и rustc 1.92 или новее), либо готовые бинарники Linux, macOS и Windows, либо brew install ivankovic/omnidiff/omnidiff, либо пакет .deb из подписанного apt-репозитория, либо nix run github:ivankovic/omnidiff. Для git запустите omnidiff git configure (или настройте difftool вручную и выберите git difftool --tool=omnidiff); для jj, omnidiff jj configure, при этом нужен режим file-by-file. Просто посмотреть два файла: omnidiff BEFORE AFTER. Для скриптов, omnidiff --headless BEFORE AFTER, а флаг --exit-code даст код 1 при различиях. Лицензия: AGPL-3.0; организациям, которым не подходят её требования об открытии исходного кода, автор предлагает коммерческую лицензию по ежемесячной подписке через GitHub Sponsors. Условия, в файле LICENSE-COMMERCIAL.

Можно ли доверять

Все цифры о скорости и надёжности, собственные измерения автора из README, независимых проверок в доступном тексте нет. Скорость замерена на одном ноутбуке (Intel Core i7-1370P), на сервере с Xeon E3-1275 v5 тот же пример занимает примерно в 2,5 раза больше времени. Показатель 99,95% описывает долю пар, успешно завершивших работу в пределах лимитов времени и памяти, а не правильность результата: за точность отвечает отдельный раздел «Accurate», которого в доступном тексте не видно. Само README сообщает, что у PDF меньше десяти оценённых тестовых случаев. Сравнения по времени с другими инструментами, такими как git diff, в доступном тексте нет. Текст источника обрывается на описании сбоев по памяти, поэтому о последующих разделах судить нельзя.

Риски и подводные камни

Лицензия AGPL накладывает обязательства по раскрытию исходного кода; для внутреннего использования в организации без них нужна платная коммерческая лицензия. Сборка из исходников требует компилятора C и занимает несколько минут. Шесть форматов (Bazel, Dart, Emacs Lisp, Markdown, Protocol Buffers, SQL) сравниваются просто как текст. Архивы не укладываются в цель по p99, а PDF в 43% случаев занимают 100 мс и больше. Расширение для VS Code пока не может заменить встроенный просмотр diff, а браузерный codereview имеет версию v0.0.0 и используется на свой риск. В git diff и git log -p интерактивный режим не откроется, там вывод всегда текстовый; для интерактивного просмотра нужен git difftool, а по SSH, терминал с выделенным псевдотерминалом (ssh -t).

«Цель OmniDiff, обработать 100% всех коммитов.»

— README проекта OmniDiff