EnglishРусский Map
Git's Magic Files

С Git'ом не всё в порядке

title
С Git'ом не всё в порядке
type
summary
summary
Аргументы Билла Джингса: примитивы Git ломаются при работе со стопками PR и асинхронной разработке из-за отсутствия истории rebase и преемников коммитов.
tags
version-control, git, jujutsu
created
2026-05-21
updated
2026-07-22
lang
ru
source_updated
2026-07-22
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

Билл Джингс написал этот текст, потому что без ума от jujutsu (jj) и хочет, чтобы люди его попробовали. Заход честный: вы не станете его пробовать, если считаете, что с Git'ом всё в порядке, а его тезис как раз в том, что с Git'ом всё не в порядке - особенно если рабочий процесс распределён во времени, между людьми или и то, и другое.

У Git две задачи: распределённое хранение исходников и инструментарий для распределённой работы. С хранением он справился настолько блестяще, что большинство пользователей просто не замечают: рабочие процессы в нём были второстепенной мыслью.

Никакого C не существует

В аккуратных диаграммах из обучающих руководств коммиты называют C1, C2, C2'. На схемах реальных репозиториев видны только хеши и имена веток. Стоит перейти к реальным именам, как выясняется, о чём Git на самом деле знает, а о чём нет:

  • Никакого понятия о коммитах-преемниках. Нет никакого "C2'", указывающего назад на "C2", - это независимые объекты, история rebase утеряна.
  • Никакой истории ревизий. Стоит сделать amend коммита, и старый коммит уже недостижим из нового.
  • Никакой истории rebase.
  • Никакого понимания, какие коммиты - мусор.

Ветки это не исправляют. История у них есть, но:

  • Они не связаны 1:1 с изменениями кода - это лишь соглашение, полагаться на него нельзя.
  • Они никак не связаны друг с другом. Из trunk нельзя надёжно найти wp/bugfix - ветка даже недостижима по прямым ссылкам.

Стопки PR в Git хрупки

Стопки изменений (stacking) помогают сохранять пропускную способность, когда задержки из-за разных часовых поясов иначе выстроили бы всю работу в одну цепочку. Пишете PR 1, отправляете, поверх PR 1 пишете PR 2, отправляете и так далее. Обычный конвейер процессора, применённый к ревью кода.

Всё становится неприятно, когда нужно сделать rebase стопки на обновлённый trunk. Хочется подтянуть trunk вперёд и сдвинуть вместе с ним всю стопку. В Git это хрупкий процесс:

  • Из более раннего коммита нельзя просто так увидеть коммиты-преемники.
  • Даже если бы было можно, эти преемники могли бы оказаться мусором.
  • Ветки "и есть" эти PR, но при таком подходе на них легко случайно наступить.

Инструменты вроде Graphite обходят это, храня метаданные веток отдельно за пределами Git, но они могут рассинхронизироваться в тот же момент, как вы тронете Git напрямую.

Отсутствие модели изменяемости

Всё это проистекает из того, как Git избегает явной модели изменяемости. Билл разбирает модель working copy / staging / unstaged / stash / HEAD. Каждая из этих сущностей - снимок или diff, существующий "в репозитории" лишь в размытом файловом смысле. Никакие изменения не переходят влево (в мир зафиксированных коммитов) без явной команды.

Любопытно, что эти шаги похожи на rebase - staging поверх HEAD, воспроизведённый относительно другой ветки. Будь коммиты изменяемыми, всё это можно было бы описать единообразно. Но они неизменяемы, так как идентификаторы коммитов - это хеши содержимого. Поэтому working copy и staging вынуждены вести себя как ветки, наследуя все описанные проблемы веток.

Цена этого:

  • Изучать Git сложнее, потому что всё существует в двух экземплярах.
  • Экспорт выглядит странно - состояние репозитория сильно отличается от того, что клонируется.
  • Асинхронные процессы, где наборы изменений эволюционируют со временем, не работают: левая сторона не умеет выражать изменения иначе как через ветки.
  • Реальный рабочий процесс порой вообще невозможно выразить: изменяемая половина не способна представить слияния.

Git не умеет выражать реальные рабочие процессы

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

  • Сделать rebase новой возможности на ветку с исправлением и протаскивать зависимость через ревью.
  • Делать rebase в процессе разработки и откатывать его перед отправкой.

Но нельзя сказать: "в моём рабочем workspace'е должен быть весь код исправления плюс весь код новой возможности, но PR должны оставаться параллельными". В jj это делается в одну строку - см. статью Айзека Корбри о мегаслияниях в jujutsu. Ровно так же проверяется совместимость новой возможности с несколькими невлитыми PR.

К чему всё сводится

Взгляд Билла таков: до появления Git проблемы систем контроля версий были очевидны всем; Subversion был плох, и никто не делал вид, что это не так. Сегодня никто не жалуется на администрирование репозиториев Git, и большинство пользователей не просят упростить управление ветками. Но для тех, чьи рабочие процессы по-настоящему распределены (а это все, кто пользуется LLM, ведь агенты как раз и создают асинхронную разработку), Git отстаёт от современного уровня уже неприлично долго. В Meta внутренние системы превосходят Git на голову уже почти десять лет.

К репликам в духе "я больше не трогаю Git, этим занимается Claude" Билл относится скептичнее всего: агенты ведут ещё больше асинхронной разработки, а не меньше, поэтому проблемы рабочих процессов лишь усугубляются. Этот тезис соседствует с agentic-coding-fatigue и clean-code-coding-agents в блоке заметок о том, как агентная разработка меняет требования к инструментам.