Jujutsu 0.45 научился сам сводить расходящиеся коммиты командой jj converge
Вышел Jujutsu 0.45.0: новая команда jj converge сама собирает один коммит из расходящихся версий одного изменения, а два ломающих изменения касаются конфигов и импорта из Git. Разбираем, что это значит для тех, кто уже перешёл на jj.

Проект Jujutsu 3 сентября в 07:47 мск выпустил версию 0.45.0 своей системы контроля версий jj, которая работает поверх обычного Git-репозитория. Главное в релизе новая команда jj converge: она находит расходящиеся версии одного изменения и заменяет их одним коммитом, сама подбирая решение и спрашивая пользователя только там, где эвристики не справились.
Для тех, кто уже пользуется jj, это закрывает самую неприятную бытовую проблему инструмента, а два ломающих изменения в том же релизе стоит проверить сразу после обновления: команды jj config с флагом --user больше не спрашивают, какой файл править, а jj git import перестал подтягивать коммиты из отсоединённого HEAD в репозиториях без совмещения с Git. Предыдущая версия 0.44.0 выходила 6 августа, релизы у проекта идут примерно раз в месяц.
Ключевые выводы
- jj converge заменяет две и больше видимых ревизии одного change ID одним коммитом; потомки перебазируются на решение, локальные закладки переезжают сами.
- Если эвристики не уверены, команда задаёт вопросы: слить описания, выбрать родителей, редко выбрать автора; в режиме --no-interactive операция прерывается, если нужен вопрос пользователю.
- Ломающее: jj config edit/set/unset --user теперь правит первый загруженный пользовательский конфиг, для точного выбора файла добавлен --file ПУТЬ.
- Ломающее: jj git import в неколоцированных репозиториях больше не импортирует коммиты с отсоединённого Git HEAD.
- Git HEAD теперь хранится отдельно для каждого worktree, существующие репозитории мигрируют автоматически; исправлен баг с duplicateEntries в git fsck после git add.
Откуда в jj берутся расходящиеся коммиты
В jj у каждого изменения есть постоянный change ID, который переживает перезапись коммита: можно поправить старый коммит в середине стека, и потомки перебазируются автоматически. Расхождение, или divergence, возникает, когда одно и то же изменение оказалось переписано дважды по разным веткам истории операций. Типичный сценарий: правка в двух рабочих пространствах, откат через jj undo после того, как часть работы уже ушла дальше, или неудачный конфликт при синхронизации с удалённым Git-репозиторием. В логе это выглядит как два коммита с одинаковым change ID, и до 0.45 их приходилось сводить руками через jj abandon, jj squash и jj rebase.
Что делает jj converge на самом деле
По описанию команды в исходниках, jj converge берёт ревизии из аргумента --revisions или из настройки revsets.converge, группирует их по change ID и считает расходящимися те группы, где ревизий больше одной. Если расходящихся изменений несколько, команда спросит, какое сводить. Дальше она применяет эвристики, чтобы собрать новую ревизию, а если те дали неоднозначный результат, задаёт вопросы: объединить ли описания, каких родителей выбрать и, в редких случаях, кого считать автором.
Когда решение найдено, новая ревизия заменяет расходящиеся, потомки перебазируются на неё, а локальные закладки, указывавшие на любую из старых версий, переезжают на новую. Два ограничения авторы оговаривают прямо. Во-первых, сводятся только ревизии, попавшие в revset: если у того же change ID есть видимые версии вне выборки, расхождение уменьшится, но не исчезнет. Во-вторых, в результате могут появиться файловые конфликты, даже если до этого их не было. Проверить, что именно сделала команда, можно через jj op show -p, посмотреть историю изменения через jj evolog, а откатить всё одним jj undo.
Режим --no-interactive нужен для скриптов и хуков: если без вопроса не обойтись, команда не зависнет в ожидании ввода, а прервётся с предупреждением.
Что сломается после обновления
Первое ломающее изменение касается конфигов. Раньше jj config edit --user, set --user и unset --user при нескольких пользовательских файлах, например ~/.config/jj/config.toml плюс каталог conf.d/, спрашивали, какой файл править. Теперь они молча берут первый загруженный. Если у вас конфиг разложен по нескольким файлам, нужный указывается явно:
Второе касается репозиториев, где jj живёт рядом с Git, но не в колоцированном режиме, то есть .jj и .git не делят один рабочий каталог. jj git import в них больше не импортирует коммиты с отсоединённого Git HEAD. Если рабочий процесс опирался на то, что jj подхватит коммит, сделанный в Git в состоянии detached HEAD, теперь такой коммит нужно закрепить веткой или тегом на стороне Git.
Внутреннее изменение, которое тоже стоит знать: состояние Git HEAD теперь хранится отдельно для каждого worktree. Это подготовка к поддержке нескольких Git worktree в колоцированных репозиториях, когда у каждого рабочего пространства jj свой HEAD. Существующие репозитории мигрируют автоматически при первом запуске новой версии.
Исправления, которые заметят те, кто уже обжигался
- В колоцированных репозиториях
git addпосле команды jj больше не оставляет в индексе устаревший cache-tree, из-за которогоgit fsckругался наduplicateEntries. Уже испорченные репозитории исправление не лечит. - Ctrl+C в пейджере
lessтеперь выходит чисто: во флаги по умолчанию добавили-K, раньше терминал мог остаться в raw-режиме с мусором из escape-последовательностей. - Сторона конфликта, оканчивающаяся на символ возврата каретки, больше не теряет этот байт при повторном разборе материализованного конфликта.
jj runостанавливается на первой ревизии, где процесс завершился с ненулевым кодом, вместо того чтобы идти дальше.- Набор
immutable_heads()по умолчанию теперь включаетuntracked_remote_tags();jj bisectпредупреждает, когда из-за пропусков не может однозначно назвать первую плохую ревизию; починен крашjj logна скрытых ревизиях с revsetlog-graph-prioritize.
Как обновиться
Готовые сборки для Windows, macOS и Linux лежат на странице релиза, вариант с musl должен работать на любом дистрибутиве. По документации установить последний релиз можно так:
Кому обновление ничего не изменит: тем, кто держит jj в одном файле конфига и работает только в колоцированных репозиториях. Им достанется jj converge и исправления, а ломающие изменения не затронут. Следующий сигнал, за которым стоит следить, это планируемая поддержка нескольких Git worktree, под которую в 0.45 подготовили хранение HEAD.
Источники: Релиз jj v0.45.0 на GitHub, Документация: установка и настройка jj 0.45, Исходник команды converge (описание в cli/src/commands/converge.rs)
Изображение на обложке: Логотип: J. Jennings, CC BY 4.0













