open atlas
↑ К треку
Git: от нуля до сеньора GIT · 08 · 05

rerere — переиспользование записанных разрешений конфликтов

rerere (REuse REcorded REsolution) записывает, как ты разрешил конфликт, и переигрывает разрешение, когда тот же конфликт появляется снова, — большой выигрыш для долгоживущих веток. Включи через rerere.enabled true; разрешения живут в .git/rr-cache. Проверяй автоприменённое.

GIT Senior ◷ 15 min
Уровень
ОсновыJuniorMiddleSenior

Ты ведёшь долгоживущую ветку и перебазируешь её на main каждые несколько дней. Каждый раз тот же конфликт в том же файле всплывает, и каждый раз ты разрешаешь его одинаково — руками, снова. Это нудная и чреватая ошибками рутина. У git есть фича, созданная ровно для этого: rerere, которая смотрит, как ты разрешаешь конфликт один раз, а потом автоматически переигрывает ровно это разрешение каждый будущий раз, когда идентичный конфликт появляется.

К концу этого урока ты сможешь включить rerere, объяснить, что и где оно записывает, и описать как сэкономленное время, так и риск тихой ошибки, от которого надо защищаться.

Цель

После этого урока ты сможешь объяснить, что делает rerere (REuse REcorded REsolution), включить его, описать, как оно записывает и переигрывает разрешения конфликтов из .git/rr-cache, назвать процессы, где оно окупается, и сформулировать риск того, что оно тихо переприменит неверное разрешение.

1

rerere расшифровывается как REuse REcorded REsolution (переиспользовать записанное разрешение). Это встроенный механизм git, решающий одну конкретную боль: разрешение одного и того же конфликта слияния снова и снова. Когда включено, git запоминает «до» и «после» каждого разрешённого тобой конфликта, привязанные к содержимому конфликта. В следующий раз, когда появляется конфликт с идентичным «до», git автоматически применяет твоё запомненное «после» — ручную работу ты делаешь один раз, а не каждый раз.

2

Включи его одной настройкой конфигурации. rerere по умолчанию выключено; включи его глобально, чтобы оно работало во всех твоих репозиториях:

git config --global rerere.enabled true

С этого момента каждое слияние или rebase, наткнувшееся на конфликт, кормит rerere: оно записывает твоё разрешение, когда ты заканчиваешь, и сверяется со своим кешем, когда появляется новый конфликт. Никаких изменений в том, как ты запускаешь merge или rebase, — оно работает тихо в фоне.

3

Разрешения хранятся в .git/rr-cache, ключ — содержимое конфликта. Для каждого конфликтующего файла git хранит «preimage» (маркеры конфликта, как их выдал git) и, как только ты его разрешишь, «postimage» (твоё разрешение) под .git/rr-cache/<hash>/. Хеш выводится из текста конфликта, поэтому сопоставление идёт по содержимому конфликта, а не по пути файла или коммиту, — именно поэтому оно может узнать «тот же конфликт» через свежий rebase, породивший новые SHA коммитов.

git rerere status     # у каких файлов есть записанный preimage в этом конфликте
git rerere diff       # как выглядит твоё текущее разрешение против preimage
4

На следующем идентичном конфликте rerere автоприменяет твоё прошлое разрешение. Когда merge или rebase заново создаёт конфликт, чей preimage совпадает с кешированным, git пишет твой записанный postimage прямо в рабочий файл и сообщает тебе:

Resolved 'app/config.ts' using previous resolution.

Маркеров конфликта уже нет; файл держит твой прежний ответ. Ты всё ещё должен сделать git add файла и продолжить merge/rebase — rerere разрешает содержимое, оно не коммитит за тебя. Если ты разрешил что-то неправильно и хочешь сбросить память для текущего конфликта, git rerere forget <path> отбрасывает её, чтобы ты разрешил заново.

Почему это работает

По-настоящему rerere окупается на долгоживущих ветках и повторных интеграциях. Фиче-ветка, живущая неделями и многократно перебазируемая на main, будет переигрывать те же конфликты на каждом rebase; rerere схлопывает это в одно ручное разрешение. То же касается мейнтейнеров, которые тест-сливают много тематических веток вместе, сбрасывают тестовое слияние и пересливают позже, — процесс, который используют сами мейнтейнеры Git. Везде, где одно и то же расхождение примиряется больше одного раза, rerere превращает N ручных разрешений в одно.

5

Риск: оно может тихо переприменить неверное прошлое разрешение — всегда проверяй. rerere ровно настолько право, насколько право разрешение, которому ты его научил. Если ты однажды разрешил конфликт неправильно, оно уверенно переприменит ту же ошибку в следующий раз, без всякого запроса, — а поскольку маркеры исчезают, это легко не заметить. Относись к автоматически разрешённому файлу как к заявлению, которое надо проверить, а не как к готовому ответу:

git diff             # осмотри каждый разрешённый rerere файл перед добавлением
git rerere forget <path>   # отбросить плохое записанное разрешение и переделать

Удобство реально, но тихо переприменённое плохое слияние — это мерзкий баг; именно дисциплина проверки результата делает rerere безопасным.

Разбор примера

Еженедельный rebase, который перестаёт повторяться.

Ты включаешь фичу и начинаешь долгоживущую ветку:

git config --global rerere.enabled true
git switch -c feature
# ...недели работы; main продолжает идти вперёд...
git rebase main
# CONFLICT в app/config.ts — ты аккуратно его разрешаешь
git add app/config.ts
git rebase --continue

За кулисами rerere сохранил preimage и твой postimage в .git/rr-cache. Несколько дней спустя main снова сдвинулась, и ты перебазируешь ещё раз:

git rebase main
# Resolved 'app/config.ts' using previous resolution.
git rerere diff           # подтвердить, что автоприменённый результат — то, что ты имел в виду
git diff                  # осмотри его как любое изменение, прежде чем доверять
git add app/config.ts && git rebase --continue

Конфликт, который ты разрешил бы вручную второй (и третий, и четвёртый) раз, был применён за тебя мгновенно. Ты всё ещё проверяешь и добавляешь его, но повторяющееся принятие решения произошло лишь однажды. Будь твоё исходное разрешение неверным, то же неверное содержимое всплыло бы здесь, — именно поэтому шаг проверки git diff не опционален; git rerere forget app/config.ts дал бы тебе начать разрешение этого файла заново.

Частая ошибка

Тонкое заблуждение — что rerere «запоминает конфликты по файлу или по ветке». Оно ключуется по содержимому конфликта (хешу preimage), а не по пути или ветке. Так что слегка иной окружающий контекст даёт другой preimage, и rerere его не сопоставит — ты будешь разрешать снова. И наоборот, идентичный конфликтующий участок в другом файле может совпасть. Это ключевание по содержимому — то, что позволяет ему пережить rebase, переписывающие SHA, но это же значит, что небольшие изменения сверху могут тихо помешать ему сработать.

Проверь себя
Викторина

Что на самом деле даёт тебе включение rerere?

Итог

rerere — REuse REcorded REsolution — записывает, как ты разрешаешь конфликт слияния, и автоматически переигрывает это разрешение всякий раз, когда идентичный конфликт появляется снова, избавляя от повторения той же работы на каждом rebase. Включи через git config --global rerere.enabled true; разрешения живут в .git/rr-cache, ключ — содержимое конфликта (поэтому оно переживает rebase, переписывающие SHA). Оно блистает на долгоживущих ветках и повторных интеграциях, осматривается через git rerere status/diff и сбрасывается по файлам через git rerere forget. Подвох: оно может тихо переприменить неверное прошлое разрешение, поэтому всегда делай git diff автоматически разрешённого файла, прежде чем доверять. С этим ты увидел git от хранилища объектов до инструментов, автоматизирующих его самую тяжёлую повседневную рутину.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.