git history: более безопасная перезапись истории в ядре git
- title
- git history: более безопасная перезапись истории в ядре git
- type
- summary
- summary
- Новая экспериментальная подкоманда git - fixup, reword, split - атомарно переписывает старые коммиты и делает rebase всех веток
- tags
- git, developer-tools, version-control
- sources
- lalitm-git-history
- created
- 2026-07-18
- updated
- 2026-07-18
- lang
- ru
- translation_of
- git-history-command
- source_updated
- 2026-07-18
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
git history - экспериментальная подкоманда, появившаяся в ядре git за два релиза: в 2.54 (апрель 2026 года) вошли reword и split, а в 2.55 (июнь 2026 года) добавился fixup. Она оформляет три частых сценария интерактивного rebase'а в виде отдельных команд с меньшим числом лишних действий. В заметке Lalit это преподносится как возможность получить часть того, что привлекает людей в jj, не уходя из git - и без установки сторонних инструментов, так как команда входит в стандартный дистрибутив.
Все три подкоманды делают одно и то же: редактируют старый коммит и пересобирают поверх него всю цепочку. Перезапись коммита меняет его хеш, поэтому каждый потомок создаётся заново с новым хешем, а указатели всех локальных веток, ссылавшиеся на этот диапазон, сдвигаются следом. Охват здесь шире, чем у git rebase --update-refs, который обновляет только ссылки внутри перебазируемого диапазона: git history находит и переписывает каждую локальную ветку, происходящую от целевого коммита, хотя область действия можно ограничить и текущей веткой.
fixup вливает проиндексированные изменения в более старый коммит. Правки индексируются через git add как обычно, после чего git history fixup <commit> вклеивает их - аналогично git commit --fixup вместе с autosquash-rebase'ом, но с одновременным обновлением всех остальных веток, содержавших этот коммит.
reword переписывает сообщение старого коммита. Команда открывает редактор с текущим сообщением; вы редактируете его, сохраняете, и стек коммитов пересобирается заново. Поскольку меняется только текст сообщения, индекс и рабочее дерево не затрагиваются вовсе.
split разбивает один коммит на два. Он запускает поблочный интерактивный выбор изменений по diff'у коммита (как git add -p): выбранные блоки (hunk'и) формируют первый коммит, а остальные попадают во второй. Это самая специфическая из трёх команд, но она избавляет от акробатики с rebase'ом, которая обычно требуется для такого разделения.
Гарантия атомарности и её цена
Все три команды объединяет свойство атомарности: операция никогда не оставляет дерево в полуразобранном состоянии посреди rebase'а. Git добивается этого простым отказом выполнять любые действия, способные привести к конфликту. Он также отказывается работать при наличии merge-коммитов, что делает команду неприменимой для ряда рабочих процессов.
По возможностям это намеренно уступает jj, где конфликты считаются полноправными сущностями (first-class), а конфликтное состояние можно протащить через rebase и разрешить позже. В документации git history прямо указано, что ограничение с отсутствием конфликтов заложено специально, поскольку перезапись истории не должна хранить промежуточное состояние. Там же отмечается, что ограничение "может быть снято, как только (если) Git научится работать с первоклассными конфликтами". Так что планка возможностей в будущем может подняться. Кроме того, reword и split оперируют исключительно графом коммитов, поэтому можно переписать коммит в ветке, которая сейчас даже не переключена, не мешая текущей работе.
git history не устраняет полностью разрыв с jj: здесь по-прежнему нет журнала операций с удобным откатом, рабочая копия не моделируется как коммит, а rebase не умеет переносить конфликты. Но эта подкоманда переносит несколько привлекательных возможностей в инструмент, которым большинство и так уже пользуется.
Это инструмент сугубо для работы с историей git, соседствующий с поведением закоммиченных файлов в git-magic-files и аналитическими подходами к истории git в pgit и linux-kernel-pgit, хотя последние лишь читают историю с помощью запросов, а не переписывают её.
Интересное из обсуждения
Краткая выжимка от nine_k: в свежих версиях git три частых сценария использования git rebase --interactive выделили в отдельные простые команды, которые работают только при полном отсутствии конфликтов.
Сквозная мысль обсуждения: это скорее более безопасная ступенька для новичков, а не замена привычным инструментам. WorldMaker отметил, что разработчикам, свободно владеющим git rebase, эта команда может и не понадобиться, но при обучении джуниоров она служит хорошим и безопасным стартом для нескольких типовых задач - грабли rebase'а новички обычно осваивают быстрее, чем его возможности. paularmstrong рассчитывает, что git history split поможет новичкам разбивать крупные PR на более мелкие, и выразил пожелание, чтобы команда умела делить надвое целую ветку.
Несколько комментаторов оспорили тезис статьи о том, что rebase - это страшно, указав на существующие механизмы защиты: git rebase --abort, reflog, синтаксис mybranch@{1} для возврата к предыдущему положению ветки и привычку создавать временную ветку или тег before-rebase перед операцией. skydhash возразил, что конфликты - это сигнал о расхождении намерений в двух коммитах, а страх перед ними обычно означает недостаточное понимание вносимых изменений. Это вылилось в долгий и острый спор о том, реальны ли проблемы UX в git или это просто гейткипинг в духе "git gud", включая реплику klibertp о том, что git - это Dark Souls от мира систем контроля версий.