Это небольшая заметка о моем форке QGit, о сделанных доработках, и о том, почему эти изменения, с высокой степенью вероятности, не попадут в кодовую базу основного репозитория.

Немного истории

При переходе с Windows на Linux мне пришлось изучать не только новую операционную систему, но и новую систему контроля версий - GIT. Очень быстро я проникся идеей о том, что работать с GIT нужно из консоли и только визуализацию дерева коммитов оставить на откуп графическим утилитам. Отсмотрев несколько графических программ, я остановил свой выбор на QGit. Утилита показалась достаточно минималистичной и симпатичной в графическом плане. К тому же, она была написана на Qt, что давало мне потенциальную возможность заглянуть под капот QGit (я - разработчик ПО и последние лет десять создаю программы с использованием Qt Framework).

Все шло своим чередом ровно до тех пор, пока разработчики GIT не решили добавить русскоязычную локализацию в свою программу. Поначалу русскоязычные сообщения от GIT выглядели немного странно, но в конечном счете привыкаешь. Однако QGit с этим радикально не хотел мириться, упорно выдавая крокозябры в своей консоли. Первое время я пытался нивелировать эту проблему консервативным способом, удаляя файл локализации после очередного обновления GIT. Через несколько месяцев мне это делать надоело. Пришло время повторно знакомиться с QGit, уже на уровне исходного кода.

Первые опыты

Вхождение в чужой проект - достаточно трудоемкое занятие. Мне потребовалось почти две недели, чтобы составить представление о том, как QGit устроен изнутри. Потраченные усилия оказались не напрасны, проблему с отображением кириллических символов удалось локализовать и исправить. Изменения были приняты в основной репозиторий QGit. Далее последовали несколько небольших доработок по улучшению юзабилити интерфейса, они тоже были приняты мантейнером. А вот предложение добавить в проект поддержку стандарта C++11 было отклонено. Мантейнер пояснил, что на данный момент есть еще много разработчиков использующих компиляторы без поддержки C++11. На дворе был 2016 год... С этого момента все доработки выполнялись в моем форке. За следующие два года было выполнено более 30 изменений, среди них пара существенных:

  • переработана реализация внутреннего кэша, используемого для построения дерева коммитов;

  • значительное ускорение построения дерева коммитов для проектов с большой историей.

При этом, форк периодически синхронизировался с основным проектом.

Рубикон

Для меня QGit, без преувеличения, программа "на каждый день" :). Частенько приходится работать в выходные или заниматься проектами подобными этому. И несмотря на то, что QGit меня практически полностью устраивал, одной функции очень сильно не хватало. Речь идет о проверке орфографии при создании комментария для коммита.

Уже в 2018 году у меня было понимание, что проверку орфографии нужно было внедрять в QGit, вопрос состоял только в том, когда и какими средствами? Реализация нового функционала и фикс мелких ошибок это совсем не одно и тоже. Вдобавок, у меня была внутренняя дилемма: нужно ли сохранять исходную кодовую стилистику проекта или же использовать более агрессивный подход по модификации кода, привнося в него сторонние библиотеки и собственные наработки в виде зависимостей. Перспективы, что мою работу примут в основную ветку, были туманны. Теоретически это могло произойти, но пришлось бы вести долгую переписку, убеждая мантейнера в ценности вносимых изменений. С другой стороны, использование собственных наработок делало рабочий процесс более комфортным и экономило время. Последний фактор оказался решающим. Так я перешел психологический "Рубикон".

Механизм проверки орфографии

Процесс проверки орфографии можно разбить на два шага:

  • определение языка проверяемого слова;

  • собственно, сама проверка орфографии с использованием словаря для языка определенного на первом шаге.

Существует несколько добротно проработанных open-source решений для детектирования языка. Даже есть реализации под GPU. К сожалению, слово "добротное" также подразумевает под собой "тяжелое" и этот факт мне сильно не нравился. Поясню в чем дело: одна из сильных сторон QGit - быстрый старт. Приложение запускается за 1-2 секунды, при этом дерево коммитов уже построено, можно работать. Это позволяет многократно открывать/закрывать QGit без потери комфорта использования. Долгая инициализация механизма проверки орфографии могла изменить это обстоятельство не в лучшую сторону, поэтому тяжелые решения были отброшены.

На момент старта работ, у меня был небольшой опыт использования библиотеки Sonnet. Её я и взял в качестве кандидата. Забегая вперед, скажу, что в проекте от Sonnet мало что осталось: пара функций по подчеркиванию слов красной волнистой линией и пара переработанных утилит по генерации языковых триграмм. Но, обо всем по порядку. Строго говоря, Sonnet не обладает функционалом для проверки орфографии, а является лишь оберткой для специализированных библиотек, таких как aspell, hunspell. С другой стороны, специализированные библиотеки не умеют определять язык проверяемых слов, это как раз и делает для них Sonnet. У меня не было намерения использовать Sonnet "как есть", от этой библиотеки мне нужен был только механизм определения языка. Даже не так, не сам механизм, а принципы его функционирования, потому что к качеству работы механизма были вопросы.

Что же не так с Sonnet? Детектирующий механизм определяет язык не для отдельного слова, а целиком для строки (фразы). Вероятно, для текста большого объема такой подход является оправданным, но у него есть существенный недостаток: если в строке встречается слово из другого языка, оно всегда будет детектироваться как ошибочное независимо от того, правильно оно написано или нет. Комментарии к коммитам как раз являются случаем, когда русскоязычные и англоязычные слова могут использоваться в одной строке. Разбирая исходный код Sonnet, я наткнулся на флаги, которые, могли активировать режим детектирования по отдельным словам. Но на тот момент это было не важно, я уже знал, что определение языка по строке - не самая большая проблема. Дело в том, что подход используемый в Sonnet не отличается высокой точностью детектирования. Список языковых триграмм насчитывает всего 300 элементов (для каждого языка), что недостаточно для надежной работы механизма. При этом, тригаммы содержат пробельные символы (прямое следствие детектирования по строке), что еще больше ухудшает ситуацию. Мой опыт эксплуатации механизма показал, что даже 3000 триграмм (без пробельных символов) могут давать сбои при детектировании русского языка. Стабильный результат был достигнут только при 5000. Английскому языку достаточно всего 2000 триграмм.

Понимая, что 300 триграмм явно недостаточно для уверенной работы, разработчики Sonnet решили подстраховаться альтернативными механизмами. Второй уровень детектирования предполагает анализ юникод-символов строки/слов на принадлежность к различным языковым группам, далее делается предположение о языке (более подробно об этом методе рассказать не могу, так как просматривал его поверхностно). Для особо тяжелых случаев существует третий уровень. Он мне особенно понравился! Слова по очереди отправляются в механизм проверки орфографии (aspell, hunspell). Если механизм проверки орфографии возвращает "успех", то запоминается язык словаря. Далее по совокупности таких проверок делается вывод о языке. Спрашивается: "Зачем тогда в начале с триграммами морочиться было!?"

В QGit используется единственный механизм - детектирование по триграммам, но в более надежном варианте (5000 элементов, триграммы без пробельных символов). Язык детектируется для каждого слова. Проверка орфографии выполняется при помощи hunspell, сейчас это наиболее популярная библиотека. Закомментированные строки не проверяются.

Список изменений

Существенные

  • проверка орфографии при создании коммита;

  • ускорение построения дерева коммитов для проектов с большой историей;

  • улучшение поддержки юникода.

Несущественные

  • закрытие окна консоли при нажатии на "пробел" (кнопка "OK" получает фокус по умолчанию);

  • при создании подписи к коммиту нажатие на Ctrl+Enter эквивалентно клику по "OK";

  • Shift+! вызывает форму для основного коммита;

  • Shift+@ вызывает форму для amend-коммита;

  • QGit завершает работу по нажатию на 'Q';

  • сохраняется ширина столбцов в дереве коммитов;

  • Tab-закладки закрываются по Alt+W;

  • список файлов виден в Init-коммите;

  • авто-перенос для длинных однострочных коммитов (отображение в несколько строк);

  • диалоговые окна с сообщениями об ошибках отображаются только когда консоль скрыта;

  • удалена панель статуса с формы консоли;

  • для дерева коммитов запрещен режим DragAndDrop;

  • для команды checkout изменена комбинация клавиш: Ctrl+Shift+C -> Ctrl+C;

  • добавлена возможность задавать размер иконок;

  • идентификатор нулевого коммита не выводится в интерфейс;

  • формат конфиг-файла изменен с INI на YAML;

  • добавлена возможность скрывать диалог подтверждения при создании коммита;

  • исправлен приоритет отображения закладок Log/Diff при обновлении дерева.

Сильно не существенные

  • система логирования заменена на ALog (нужно для системы YAML-конфигурирования).

Дистрибутивы

Код форка расположен тут. Собранный пакет под Ubuntu 20.04 можно взять здесь. Также есть standalone-пакет под Ubuntu 18.04/20.04 содержащий Qt-компоненты и hunspell (устанавливается в /opt).

Минорное заключение

Любой новый функционал, к сожалению, привносит и новые ошибки, это неизбежный процесс. Мои доработки не исключение. Что-то я уже обнаружил сам и исправил, что-то придется найти вам, если конечно решите использовать мой форк.