Git history: удобнее править старые коммиты

15.07.2026 · 5 мин

Меня всегда напрягало одно в Git: чтобы исправить опечатку в коммите трехнедельной давности, нужно танцевать с бубном вокруг rebase -i. Помните это чувство, когда вы открываете интерактивный rebase и внезапно осознаете, что одно неверное движение — и ваша ветка истории улетает в космос?

Так вот, в августе 2024 года в Git добавили команду, которая решает эту проблему изящнее. Называется она git history — и это одна из тех фичей, о которых знают единицы, хотя лежат они буквально у всех под носом.

Что за git history

git history — это экспериментальная команда, которая появилась в двух релизах Git: версия 2.54 (апрель 2024) принесла подкоманды reword и split, а версия 2.55 (июнь 2024) добавила fixup. Забавно, что каждый релиз получил порцию внимания в день выхода, а потом… тишина. И это при том, что команда уже сейчас дает многое из того, ради чего народ обычно смотрит в сторону альтернатив вроде jj.

Главное преимущество — атомарность. git history никогда не оставит ваше дерево в полу-сломленном состоянии. Как? Очень просто: команда просто откажется выполнять операцию, если есть риск конфликта. Меньше магии, чем в jj, но зато предсказуемо.

Три кита: fixup, reword, split

Fixup — чиним старые коммиты

Допустим, вы сделали коммит B, а потом поверх него еще несколько (C, D). И тут выясняется, что в B была ошибка — опечатка в сообщении логина, кривой форматирование, в общем, что-то, что нужно поправить.

Раньше вы бы делали git commit --fixup, потом git rebase -i --autosquash, молились, чтобы не было конфликтов, и надеялись, что ветки правильно встанут на место.

Теперь? Стадите исправление как обычно (git add), а потом:

git history fixup B

И всё. Магия в том, что git history не просто squashит изменения — он автоматически перенесет все ваши ветки, которые содержали этот коммит.

ДО git history fixup B
───────────────────────
          feat-1
            ▼
main ── A ── B ── C ── D
                  ▲
               feat-2

ПОСЛЕ git history fixup B
──────────────────────────
                 feat-1
                   ▼
main ── A ── B* ── C* ── D*
                  ▲
               feat-2

B* — это B с исправлением
C*, D* — пересозданы наверху
Ветки feat-1 и feat-2 поехали следом
Fixup: коммит исправлен, ветки на месте

Если быть точным, git history находит все локальные ветки, которые произошли от коммита B, и перемещает их вместе с исправлением. Это дальше, чем git rebase --update-refs, который двигает только рефы внутри диапазона активного rebase.

Есть ложка дегтя: команда не работает с merge-коммитами. Если вы активно их используете — это может стать проблемой.

Reword — правим сообщения

Иногда код остается тем же, а вот понимание задачи меняется. Особенно это случается в long-running-ветках, где дизайн эволюционирует, а commit message по умолчанию остается «WIP» или «fix stuff».

git history reword — это fixup, но для сообщений:

git history reword B

Команда откроет редактор с текущим сообщением коммита B, вы его исправите, сохраните — и всё остальное перестроится сверху. Работает чисто на графе коммитов, не трогая ваш working tree или индекс. Можно даже редактировать сообщение на коммите в ветке, которую сейчас не выбрана.

Split — разделяем коммиты

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

git history split открывает интерактивный режим построчно:

git history split B

Вы видите diff коммита B и выбираете, какие куски войдут в первый новый коммит, а какие — во второй. Это эквивалент git add -p, но без необходимости делать reset и снова собирать историю.

ДО split: один коммит с двумя изменениями
─────────────────────────────────────────
main ── A ── B (рефакторинг + новый эндпоинт) ── C

ПОСЛЕ split: два отдельных коммита
──────────────────────────────────
main ── A ── B1 (рефакторинг)
              B2 (новый эндпоинт) ── C*
Split: один коммит превращается в два логических

Сравнение с jj

Тут нужно быть честным. jj (или jujutsu, если вам так привычнее) — это действительно другой уровень. Операционный лог с легким undo, модель working copy как коммита, возможность протаскивать конфликты через rebase — всё это git history не умеет и не пытается.

Автор статьи (Lalit Maganti) описывает свой опыт точно: каждые три месяца он пробует jj, неделю пытается встроить в рабочий процесс, а потом возвращается к git. Я себя узнаю в этом описании.

Но git history дает часть преимуществ jj без необходимости менять весь workflow. Не нужно ничего устанавливать — команда уже в вашем Git. Не нужно переучиваться — синтаксис знакомый. И работает атомарно, что снимает главный страх переписывания истории.

Практический вывод

git history — это не замена jj и не революция. Это эволюция внутри привычного Git, которая делает болезненные операции менее болезненными.

Попробуйте git history fixup. Пара вечеров практики — и выйдете на новый уровень работы с историей.

Git потихоньку впитывает лучшие идеи из альтернатив. И это, как по мне, хороший знак.

Ссылки

Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.