rerere — переиспользование записанных разрешений конфликтов
rerere (REuse REcorded REsolution) записывает, как ты разрешил конфликт, и переигрывает разрешение, когда тот же конфликт появляется снова, — большой выигрыш для долгоживущих веток. Включи через rerere.enabled true; разрешения живут в .git/rr-cache. Проверяй автоприменённое.
Ты ведёшь долгоживущую ветку и перебазируешь её на main каждые несколько дней. Каждый раз тот же конфликт в том же файле всплывает, и каждый раз ты разрешаешь его одинаково — руками, снова. Это нудная и чреватая ошибками рутина. У git есть фича, созданная ровно для этого: rerere, которая смотрит, как ты разрешаешь конфликт один раз, а потом автоматически переигрывает ровно это разрешение каждый будущий раз, когда идентичный конфликт появляется.
К концу этого урока ты сможешь включить rerere, объяснить, что и где оно записывает, и описать как сэкономленное время, так и риск тихой ошибки, от которого надо защищаться.
После этого урока ты сможешь объяснить, что делает rerere (REuse REcorded REsolution), включить его, описать, как оно записывает и переигрывает разрешения конфликтов из .git/rr-cache, назвать процессы, где оно окупается, и сформулировать риск того, что оно тихо переприменит неверное разрешение.
rerere расшифровывается как REuse REcorded REsolution (переиспользовать записанное разрешение). Это встроенный механизм git, решающий одну конкретную боль: разрешение одного и того же конфликта слияния снова и снова. Когда включено, git запоминает «до» и «после» каждого разрешённого тобой конфликта, привязанные к содержимому конфликта. В следующий раз, когда появляется конфликт с идентичным «до», git автоматически применяет твоё запомненное «после» — ручную работу ты делаешь один раз, а не каждый раз.
Включи его одной настройкой конфигурации. rerere по умолчанию выключено; включи его глобально, чтобы оно работало во всех твоих репозиториях:
git config --global rerere.enabled trueС этого момента каждое слияние или rebase, наткнувшееся на конфликт, кормит rerere: оно записывает твоё разрешение, когда ты заканчиваешь, и сверяется со своим кешем, когда появляется новый конфликт. Никаких изменений в том, как ты запускаешь merge или rebase, — оно работает тихо в фоне.
Разрешения хранятся в .git/rr-cache, ключ — содержимое конфликта. Для каждого конфликтующего файла git хранит «preimage» (маркеры конфликта, как их выдал git) и, как только ты его разрешишь, «postimage» (твоё разрешение) под .git/rr-cache/<hash>/. Хеш выводится из текста конфликта, поэтому сопоставление идёт по содержимому конфликта, а не по пути файла или коммиту, — именно поэтому оно может узнать «тот же конфликт» через свежий rebase, породивший новые SHA коммитов.
git rerere status # у каких файлов есть записанный preimage в этом конфликте
git rerere diff # как выглядит твоё текущее разрешение против preimageНа следующем идентичном конфликте 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 ручных разрешений в одно.
Риск: оно может тихо переприменить неверное прошлое разрешение — всегда проверяй. 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-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.