Как я создал супер-быстрый Git-интерфейс в стиле WebStorm и зачем он мне понадобился

Непревзойденное удобство Git в JetBrains (и какую цену мы за него платим)

Если вы когда-либо писали код в WebStorm, IntelliJ IDEA или PyCharm, то наверняка знаете: в JetBrains создали эталонный, невероятно удобный рабочий процесс для работы с Git.

Дело не просто в добавлении файлов в индекс — дело в многолетней мышечной памяти:

  • Changelists (Списки изменений): Возможность логически группировать файлы по задачам, отделять эксперименты от срочных хотфиксов и коммитить только нужную часть работы без утомительного git add -p.
  • Git Shelf (Локальная полка): Откладывание незавершенной работы («зашелвить») с понятными описаниями и комментариями, оставляя рабочую директорию чистой и не засоряя анонимными записями git stash.
  • Интерактивный Diff-редактор: Сравнение файлов side-by-side с возможностью прямо в окне диффа редактировать файл рабочей копии и сохранять по Cmd+S, а также откатывать отдельные куски (hunks) одним кликом по стрелке в гаттере (↩).
  • Эргономика горячих клавиш: Перемещение между различиями по F7 / Shift+F7 и моментальный коммит по Cmd+Enter.

В чем проблема: налог на тяжеловесность

Несмотря на всю любовь к этому воркфлоу, меня постоянно преследовало раздражение. IDE от JetBrains — это огромные комбайны на JVM. Запуск WebStorm только ради того, чтобы посмотреть дифф, зашелвить пару файлов или сделать чистый аккуратный коммит, занимает от 15 до 30 секунд и сжирает 1.5–3 ГБ оперативной памяти.

Когда я пишу небольшие скрипты в терминале, редактирую конфиги в легких редакторах или переключаюсь между десятком микросервисных репозиториев, открывать полноценную IntelliJ IDEA ради пары кликов в Git — все равно что заводить тяжелый самолет, чтобы просто доехать до соседнего магазина.

Я перепробовал все популярные альтернативы:

  • VS Code: Встроенная работа с Git лишена полноценных ченджлистов, нет аналога Shelf, а диффы не дают интерактивно откатывать конкретные куски кода удобными маркерами в гаттере.
  • GitKraken: Сделан на Electron. Потребляет 700 МБ–1.2 ГБ RAM, запускается по 4–6 секунд, а базовые фичи прячет за платной подпиской.
  • SourceTree: Медленный, визуально перегруженный и морально устаревший интерфейс.
  • Sublime Merge: Очень быстрый, но в нем нет концепции ченджлистов, редактирования кода прямо внутри диффа и удобного шелвинга.

Тогда я задал себе простой вопрос:

А можно ли сделать отдельный автономный Git-клиент для macOS, который запускается меньше чем за 150 миллисекунд, потребляет всего ~35 МБ памяти и дает ровно тот самый любимый воркфлоу WebStorm?

Так родился проект ultra Git. Ниже — подробная история того, как устроена архитектура приложения, какие инженерные грабли встретились на пути и как удалось добиться такой скорости.


Архитектура: почему мы отказались от Electron в пользу Swift + WKWebView

Когда сегодня разрабатывают кроссплатформенные десктопные инструменты, первым делом берут Electron. Однако у Electron огромная цена: каждое окно тащит с собой целый экземпляр браузера Chromium и процесс Node.js. В итоге установщик весит 200+ МБ, в памяти приложение занимает от 400 МБ, а холодный старт заставляет ждать.

Чтобы на macOS приложение открывалось мгновенно и не расходовало ресурсы ноутбука, я выбрал гибридную нативную архитектуру:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌────────────────────────────────────────────────────────┐
│ ultra Git (macOS) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Нативная оболочка на Swift (бинарник 75 КБ) │ │
│ │ - Окно NSWindow и системный компонент WKWebView │ │
│ │ - Холодный старт: < 150 мс | RAM: ~35 МБ │ │
│ └─────────────────────────┬────────────────────────┘ │
│ │ загружает http://localhost│
│ ┌─────────────────────────▼────────────────────────┐ │
│ │ Интерфейс React 18 + TypeScript + Vite │ │
│ │ - Тема JetBrains Darcula / Dark │ │
│ │ - Движки диффов Monaco и CodeMirror 6 │ │
│ │ - Ченджлисты, Полка (Shelf), Вкладки проектов │ │
│ └─────────────────────────┬────────────────────────┘ │
│ │ REST / WebSocket │
│ ┌─────────────────────────▼────────────────────────┐ │
│ │ Локальный микро-сервер Node.js │ │
│ │ - Прямое выполнение команд CLI git и файловой │ │
│ │ системы │ │
│ │ - Ченджлисты: .git/ultra-changelists.json │ │
│ │ - Полка: .git/ultra-shelf/*.patch │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

1. Нативный контейнер на Swift (macos/main.swift)

Приложение для macOS написано на чистом Swift без сторонних зависимостей:

  • Создает нативное окно NSWindow с темным оформлением и аккуратными системными кнопками управления.
  • Встраивает компонент Apple WKWebView с аппаратным ускорением графики.
  • Автоматически поднимает локальный бэкенд-процесс при старте.
  • Скомпилированный бинарник весит всего 75 КБ, стартует за менее чем 150 миллисекунд и потребляет в покое всего ~35 МБ оперативной памяти.

2. Локальный Git микро-сервер

Вместо использования тяжелых нативных C-библиотек вроде libgit2 (которые регулярно вызывают проблемы со сборкой на разных архитектурах процессоров и не учитывают пользовательские конфиги), бэкенд общается напрямую со стандартным системным git CLI:

  • Автоматически подхватывает ваш существующий .gitconfig, SSH-ключи, GPG-подписи и алиасы.
  • Все операции (стейджинг, диффы, шелв, коммиты) выполняются стандартными проверенными командами Git без скрытых сайд-эффектов.

Воплощение возможностей WebStorm в жизнь

1. Ченджлисты: осознанный стейджинг файлов

В стандартном Git файлы либо не отслеживаются, либо изменены, либо находятся в индексе. JetBrains превратил работу с изменениями в удовольствие с помощью Changelists — виртуальных списков файлов, позволяющих разделить текущую работу на независимые части:

  • Default changelist: активная работа, готовая к ревью и коммиту.
  • Пользовательские ченджлисты (например, Рефакторинг авторизации, Временные тесты, Правки в доках).

В ultra Git ченджлисты сохраняются в файле .git/ultra-changelists.json. Любой измененный файл можно перетащить мышкой (drag & drop) в другой список или переместить через контекстное меню.

При нажатии Cmd+Enter ultra Git берет только выбранные галочками файлы конкретного ченджлиста, аккуратно индексирует их и создает коммит.

2. Локальная полка (JetBrains Shelf)

В отличие от git stash, который работает как слепой стек по всему репозиторию сразу, функция Shelve позволяет:

  1. Выбрать конкретные файлы или целый ченджлист.
  2. Задать понятное текстовое описание.
  3. Сохранить их на диск в виде отдельного патча.
  4. В любой момент просмотреть дифф отложенных изменений прямо в интерфейсе.
  5. Вернуть изменения обратно в рабочую копию (Unshelve) в один клик (с возможностью опции Keep in shelf, чтобы сохранить патч для повторного применения).

В ultra Git все шелвы лежат в папке .git/ultra-shelf/ в виде чистых .patch файлов с метаданными в JSON. Они изолированы, не засоряют историю git reflog и не конфликтуют с удаленными ветками.

3. Вкладки проектов и история недавних репозиториев

Разработчики редко сидят в одном репозитории. В верхней панели ultra Git встроены вкладки с переключением за 0 миллисекунд:

  • Параллельное открытие нескольких проектов.
  • Быстрый доступ к 10 последним открытым репозиториям.
  • Дерево файлов с ленивой подгрузкой, которое мгновенно открывает даже огромные монорепозитории с десятками тысяч файлов.

Редактор диффов: технические вызовы и решения

Сердце любого Git-клиента — это просмотр диффов. Создание по-настоящему удобного редактора изменений оказалось самой интересной инженерной задачей проекта.

1
2
3
4
5
6
7
8
9
      Оригинал (HEAD)                Модифицированный (Рабочая копия)
┌─────────────────────────────┬───────────────────────────────────┐
│ 12 const port = 3000; │ 12 const port = 8080; [↩] │
│ 13 const host = "0.0.0.0"; │ 13 const host = "0.0.0.0"; │
│ │ 14 const debug = true; [↩] │
└─────────────────────────────┴───────────────────────────────────┘
▲
Редактируемая область
(Cmd+S сохраняет на диск)

Вызов 1: Баг виртуализации отступов в CodeMirror 6

Первая версия редактора была построена на CodeMirror 6 с кастомными расширениями ViewPlugin для отрисовки стрелок отката (↩) в колонке номеров строк (гаттере).

Здесь мы натолкнулись на неприятную особенность: CodeMirror 6 агрессивно виртуализирует DOM-узлы за пределами экрана. При выравнивании параллельных панелей диффа с разным количеством строк движок вставляет пустые спейсеры (spacer elements). При быстрой прокрутке больших файлов (на 2000+ строк) расчет высоты спейсеров сбивался, и правая колонка внезапно становилась пустой серой областью!

Решение: Мы переписали расчет координат высоты строк, стабилизировали спейсеры через проекцию абсолютных координат и добавили автоматическое центрирование скролла на первом изменившемся блоке при открытии любого файла.

Вызов 2: Полностью автономный офлайн-запуск Monaco Editor

Многие разработчики привыкли к движку Monaco Editor (на котором работает VS Code) за его подсветку синтаксиса, миникарту и работу с мультикурсорами.

Однако библиотека @monaco-editor/react по умолчанию загружает ресурсы Monaco из публичных CDN. Для локального инструмента разработчика недопустимо зависеть от подключения к интернету.

Мы полностью собрали Monaco локально через Vite. Чтобы веб-воркер сборщика корректно отрабатывал без сетевых запросов, мы настроили инстанцирование воркера через нативный синтаксис ES URL:

1
2
3
4
5
6
7
8
9
// Конфигурация локального офлайн веб-воркера Monaco
window.MonacoEnvironment = {
getWorker(_: any, label: string) {
return new Worker(
new URL('../../../node_modules/monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url),
{ type: 'module' }
);
}
};

Вызов 3: Коллизия URI моделей и баг «0 различий»

После первого подключения DiffEditor из Monaco проявился коварный баг: файлы с очевидными изменениями отображали «0 diffs», а обе панели показывали либо пустой текст, либо одинаковый контент.

Изучение исходников Monaco показало причину: редактор кэширует модели глобально по их URI. При переключении между файлами обертка @monaco-editor/react повторно использовала анонимный URI по умолчанию (inmemory://model/1). Monaco считал, что модель не поменялась, и отдавал старую кэшированную пустую модель!

Мы решили проблему явной изоляцией URI для каждого открываемого файла:

1
2
3
4
5
6
7
8
9
const originalUri = monaco.Uri.parse(`inmemory://diff/${safeFilePath}/original`);
const modifiedUri = monaco.Uri.parse(`inmemory://diff/${safeFilePath}/modified`);

let originalModel = monaco.editor.getModel(originalUri)
|| monaco.editor.createModel(originalContent, language, originalUri);
let modifiedModel = monaco.editor.getModel(modifiedUri)
|| monaco.editor.createModel(modifiedContent, language, modifiedUri);

diffEditor.setModel({ original: originalModel, modified: modifiedModel });

После этого расчет диффов заработал со 100% стабильностью: появился мгновенный подсчет измененных блоков через Chunk.build и удобная навигация по клавишам F7 и Shift+F7.

Вызов 4: Синхронизированный поиск по коду (Cmd+F)

Стандартный браузерный поиск не видит текст внутри виртуализированных строк редактора. Мы реализовали кастомную строку поиска поверх редактора:

  • Поиск ведется одновременно по обоим буферам: и в оригинальном (HEAD), и в модифицированном.
  • Показывается общий счетчик совпадений (например, 3 из 14).
  • Кнопки «Вперед» и «Назад» плавно скроллят нужный редактор и подсвечивают найденные строки.

Сравнение производительности в цифрах

Вот как ultra Git показывает себя в реальной повседневной работе на MacBook с Apple Silicon (M-серия):

Параметр WebStorm Git GitKraken VS Code Git ultra Git
Холодный старт ~18.5 сек ~4.2 сек ~2.8 сек < 0.18 сек (180 мс)
Память в покое (RAM) ~2100 МБ ~780 МБ ~450 МБ ~35 МБ
Размер нативного бинарника ~1.4 ГБ ~280 МБ ~310 МБ 75 КБ (Swift)
Редактирование прямо в диффе ✅ Да ❌ Нет ⚠️ Частично ✅ Да (Cmd+S)
Откат отдельных кусков в гаттере ✅ Да ❌ Нет ❌ Нет ✅ Да (↩)
Полноценный Git Shelf ✅ Да ❌ Нет ❌ Нет ✅ Да
Поддержка Changelists ✅ Да ❌ Нет ❌ Нет ✅ Да

Что дальше и как поучаствовать 🤝

Разработка ultra Git доказала: современный веб-стек в сочетании с нативными системными возможностями операционной системы не обязан быть прожорливым монстром. При грамотном подходе можно получить визуальное богатство современных редакторов кода со скоростью нативного C- или Swift-софта.

Проект полностью открыт и разрабатывается с прицелом на чистоту и модульность архитектуры. В планах:

  • Визуальный граф коммитов и история веток (Git Log)
  • Окно произвольного сравнения файлов с ветками и ревизиями
  • Интерактивное разрешение 3-way конфликтов при слиянии (Merge Conflicts)
  • Переключатель тем оформления (Светлая JetBrains Light, классическая Darcula, Catppuccin)

Если вы цените быстрые и отзывчивые инструменты для разработчиков — заходите в репозиторий, пробуйте на своих проектах и присылайте Pull Request!