31.10.2026 796 материалов

NecroGit «оживляет» удалённый код на локальной Gemma 2 — но где здесь археология, а где театр?

Проект NecroGit разбирает историю git-репозитория, находит удалённые функции внутри живых файлов и заставляет локальную Google Gemma 2 писать им «некрологи». Разбираемся, что здесь настоящая инженерия, а что хэллоуинский перформанс.

NecroGit «оживляет» удалённый код на локальной Gemma 2 — но где здесь археология, а где театр?

Удалённая функция действительно остаётся в объектах git, и её можно достать без облака — но «спиритический сеанс» на Gemma 2 производит не факты, а правдоподобный текст, и без внешней проверки он так и остаётся текстом.

Предыстория, вокруг которой построен весь проект, заслуживает отдельного разговора. Функция fast_inverse_sqrt() из Quake III — знаменитый трюк: чтобы получить обратный квадратный корень (то есть 1/√x), исходное число с плавающей запятой переинтерпретируют как целое, вычитают его из «магической константы» 0x5f3759df и уточняют результат одной-двумя итерациями метода Ньютона-Рафсона. Поясним для ясности: это грубое приближение 1/√x, дешёвое в вычислении, и в играх 1990-х оно нужнолось для нормализации векторов — операции, которая выполняется миллионы раз за кадр.

Алгоритм пришёл из мира 3DNow! и SGI, автором считается Гэри Таролли, а широкую известность ему принесла версия в коде id Software. Легенду дополняет комментарий // what the fuck? над второй итерацией уточнения — впрочем, кто именно его написал, история умалчивает, и уверенно приписывать его Джону Кармаку нет оснований. Ещё один нюанс: константа 0x5f3759df из Quake III не оптимальна. Крис Ломонт в своём разборе показал, что 0x5f37642f даёт чуть меньшую ошибку, а в AMD 3DNow! использовалось иное приближение вовсе. Фокус был не в магическом числе, а в том, что этот кусок кода выглядит как запрещённый приём, хотя работает.

Что конкретно построено

NecroGit — это инструмент для разбора git-истории, оформленный как «некромантия»: он ищет в репозитории мёртвый код и устраивает ему цифровые поминки. У проекта две поверхности. Первая — консольный инструмент с командами --scan и --seance, который раскапывает объекты .git и выдаёт «свидетельства о смерти» функций. Вторая — браузерный «алтарь»: HTML5-холст с 45 лепестками цветов, анимированные свечи и звуковой синтезатор на Web Audio API.

Часть с оформлением сделана аккуратно и технически интересно. Вместо mp3-файлов звук генерируется процедурно: осциллятор на 60 Гц через резкий полосовой фильтр имитирует гул строчного трансформатора ЭЛТ-монитора, буфер розового шума с модуляцией низкочастотного фильтра — ветер, затухающие синусоиды с гармониками — удары в колокол. Никаких ассетов, никаких запросов в сеть.

Но главная часть — не картинки. Это движок разбора истории.

AST-археология вместо git log

Стандартный git log --diff-filter=D умеет находить только целиком удалённые файлы. Между тем при рефакторинге функции чаще всего исчезают внутри файлов, которые продолжают жить. Утверждение «более 85% удалений происходят внутри уцелевших файлов» проект приводит без ссылки на исследование, так что цифру стоит считать оценочной, но направление мысли верное: файл-уровневый поиск действительно почти бесполезен для истории функций.

NecroGit обходит это через разбор патчей (git log -p -M) и выделение сигнатур по синтаксису: -def и -class для Python, стрелочные функции и объявления для JavaScript/TypeScript, fn, func, сигнатуры в стиле C для Rust, Go и C. Важно, что это не полноценный парсер абстрактного синтаксического дерева (AST), а скорее поиск по сигнатурам в диффах — разница принципиальная: AST-подход строит формальное дерево кода, здесь же работает сопоставление строк. Для задачи «вспомнить, что удалилось и когда» такого хватает, но на сложных конструкциях — вложенных функциях, макросах, генерируемом коде — такой парсер неизбежно будет давать ложные срабатывания и пропуски.

На выходе инструмент даёт неплохой набор данных: точное число потерянных строк, коммит рождения, хеш коммита смерти, имя автора и время удаления. Это ровно то, что обычно теряется: при просмотре старого кода вы помните, что функция «лечила какой-то баг», но вряд ли вспомните хеш коммита, в котором она исчезла.

Gemma 2 на локальной машине

Дальше включается языковая модель. NecroGit обращается к Google Gemma 2 (2B или 9B) через локальный Ollama по его HTTP-интерфейсу с потоковой генерацией. Промпт обязывает модель называть реальный хеш коммита и автора, выражать «скорбь» по поводу удаления и сочинять четырёхстрочную мексиканскую калаверу — шуточный надгробный стих, популярный в День мёртвых.

Здесь же — самое инженерно зрелое решение проекта: FactValidator. Модель склонна выдумывать, а маленькие открытые модели — особенно. В тестах Gemma 2 2B при повышенной «температуре» генерации охотно приписывала удаление чужому коду Линуса Торвальдса с коммитом-месседжем «refactor: legacy bloat». Ассоциативная логика проста: «удалённый код» — значит кто-то знаменитый. Поэтому вывод сверяется с фактическими данными git, а при несовпадении хеша или автора генерация обрывается, и включается детерминированный «эвристический некромант» без модели. Заявленное время переключения — 0,02 мс. Плюс ко всему это решает проблему доступности: если Ollama не установлен или лежит, утилита не падает.

Принципиальный аргумент проекта — приватность: история git содержит выброшенные API-ключи, неудачные коммерческие решения и архитектурные эксперименты, и гонять всё это через облачные API действительно опрометчиво. Локальный запуск означает нулевой исходящий трафик, нулевую стоимость на тысячи прогонов и работоспособность без сети. Для анализа тысяч коммитов — у Linux их больше миллиона, у Express.js около шести тысяч — это не маркетинг, а практическая необходимость.

Где заканчивается археология и начинается спектакль

Разберёмся с главным спорным местом.

Во-первых, демо-вывод «сеанса» по fast_inverse_sqrt() — это не результат раскопок, а отрепетированная сцена. В тексте говорится, что функция «убита» 10 декабря 1999 года в коммите c51b980a, где Кармак якобы заменил магическую константу на SSE-инструкцию rsqrtss. Проблема в том, что публичная git-история исходников Quake III не восходит к 1999 году с сохранёнными хешами: репозитории с открытыми исходниками id — это поздние импорты, и дата коммита в таком дереве не может служить доказательством того, что Кармак сделал эту замену именно тогда и именно с этим хешем. Хеш c51b980a стоит проверять самостоятельно — он относится к конкретному дереву, а не к «истории Quake III» вообще.

Во-вторых, SSE-версия rsqrtss — это не то же самое, что удалить функцию. Замена битового трюка аппаратной инструкцией появилась позже, когда SSE стал массовым: инструкция делает ту же операцию за один такт, но с меньшей точностью и без итераций Ньютона. Разумный рефакторинг, а не убийство легенды. Кстати, вопрос точности здесь не праздный: rsqrtss возвращает результат с относительной погрешностью порядка 1.5×10⁻³, и для игровой физики это обычно приемлемо, но не для вычислений, где важны гарантии IEEE-754.

В-третьих, метрики. Таблица с «100% точностью хешей» и «100% подавлением галлюцинаций» получена с помощью собственного FactValidator-а на собственной же выборке из десяти проверок. Это ожидаемый результат: если система обрывает любой ответ с неверным хешом и подставляет детерминированный шаблон, то «доля верных хешей» по определению будет 100% — вопрос только в том, какая доля ответов при этом ушла в шаблон. Процент отбраковки проект не приводит, а без него цифры мало что значат. Сравнение «4 артефакта против 1» в бенчмарке по раскопкам построено на одной маленькой выборке и выглядит как демонстрация принципа, а не как измерение производительности.

Отдельно отметим разумную мелочь, которую часто упускают в подобных проектах: различие MIME-типов. mimetypes.guess_type() берёт данные из системы, и на Debian/Ubuntu .js определяется как application/javascript, а на macOS — как text/javascript (RFC 9239). Тесты падали на одной ОС и проходили на другой. Это иллюстрация того, что «нулевые зависимости» не означают «нулевую цену»: вместо чужих библиотек вы разбираетесь с системным окружением сами. Ещё один баг из той же серии — ложное срабатывание парсера на Markdown: строки вида - ## Option B: Sovereign распознавались как объявления функций, потому что решётка совпадала с границей комментария. Исправление — белый список расширений кода.

Что здесь реально полезно

Если отложить в сторону хэллоуинскую упаковку, остаётся несколько содержательных идей.

Первая — поиск удалённого кода внутри живых файлов через разбор диффов. Инструменты уровня git log --diff-filter=D действительно слепы к такому, а в больших кодовых базах это основной объём потерь.

Вторая — связка «локальная модель + проверка фактов + детерминированный запасной вариант». Архитектура с обрывом генерации при несовпадении с эталоном — рабочий способ бороться с галлюцинациями маленьких моделей без тонкой настройки весов. Ограничение понятно: проверять можно только то, что уже известно из git, поэтому модель ничего не открывает — она оформляет известное.

Третья — процедурный звук и отсутствие ассетов. Мелочь, но показательная: проект действительно собирается из чистого Python и браузерных API.

Чего в проекте нет — так это скромности в цифрах и оговорках. «Стоимость $0 за 1000 сеансов» не учитывает ни электричество, ни ноутбук, на котором Gemma 2 9B с трудом поместится в памяти; заявленные 48 токенов в секунду — величина, сильно зависящая от железа, и без указания процессора и квантизации эти цифры ни о чём не говорят.

В итоге перед нами добротная учебная работа с одной сильной инженерной идеей — локальным анализом git-истории с защитой от выдумок — и большим количеством театра вокруг. Сеанса с призраком Кармака не было: была модель, которой показали реальные хеши и попросили красиво оформить. Проверяйте хеши сами — в обоих смыслах.


✅ Все упомянутые компании и проекты — id Software, Google, OpenAI, Anthropic — использованы исключительно в контексте технического описания их продуктов.