- PVSM.RU - https://www.pvsm.ru -

Привет! Я старший fullstack-разработчик в крупной b2b-команде, где мы активно развиваем IT турпродукты и сопровождаем легаси-проекты. Недавно мне довелось временно заменить тимлида — он ушёл в отпуск, оставив напоследок фразу: «Ты не будешь деплоить».
Спойлер: деплоил. И не просто деплоил, а чуть не похоронил релиз из-за одного неосторожного git reset --hard. К счастью, всё закончилось хорошо — но пришлось восстановливать ветки из GitLab’а, бороться с удалённой историей и вручную черри-пикать задачи.
Рассказываю, как всё было, какие выводы сделал и чего теперь точно делать не буду. Надеюсь, кому-то это сэкономит пару нервных клеток.
Понедельник. Утро. Просыпаюсь в нормальном настроении, пью кофе, открываю ноутбук и захожу на утренний дейлик. Тимлид ушёл в отпуск, оставив мне временно исполнять обязанности старшего — я и так senior fullstack, но теперь фактически главный по команде.
Перед уходом он, с лёгкой иронией, сказал:
«Ты не будешь деплоить».
Как показало утро — деплоить как раз мне и придётся.
На дейлике быстро становится ясно: релиз всё-таки на мне.
Это не что-то экстраординарное — раньше я релизил в прод, но уже больше года этим не занимался. За это время команда, процессы и репозиторий изменились.
Я смотрю на текущую релизную ветку, которую успел собрать тимлид, и понимаю — её нужно разделить на две части:
Релиз с накопившимися небольшими задачами, не входящими в эпики — чтобы быстрее выгрузить то, что уже протестировано и готово.
Отдельный релиз, содержащий только один эпик — он всё ещё нестабилен, требует доработок и не должен попадать на прод случайно.
Обсуждаю идею с командой — получаю согласие. План простой:
откатить релизную ветку до нужного состояния;
по частям пересобрать два релиза;
залить по очереди.
Выглядит буднично и несложно. И я, не особенно напрягаясь, приступаю.
В какой-то момент — почти на автопилоте — я выполняю команду:
git reset --hard origin/master
Моя мысль была простая: «обнулю релизную ветку, и соберу всё по новой, руками доберу нужные задачи».
Но спустя минуту я начинаю искать те самые ветки, из которых пришли задачи, чтобы пересобрать релиз...
…а их нет.
Тут я вспоминаю один спор с тимлидом. Мы долго обсуждали, стоит ли удалять ветки после мержа. В итоге пришли к соглашению: если задача смержена в релизную ветку — удаляем ветку.
В прод это попадёт из релиза, а значит, в git у нас не будет лишнего хлама.
Но: релизная ветка — это не master, это временная сборка, которая может быть переписана.
А я только что затёр её состояние хард-ресетом.
Истории изменений нет.
Оригинальные ветки удалены.
git log ничего не показывает — релизная ветка теперь просто копия master.
git reflog — локальная история, но она не спасает, если ветка была перезаписана и пушнута с --force.
Всё, что было смержено и ещё не ушло на прод — оказалось уничтожено.
А продакшен-готовый релиз — должен быть сегодня.
Паника? Немного. Но я начинаю думать, где остались следы.
В голове быстро оформляется три возможных способа хоть как-то восстановить утраченные изменения:
Проверяю, не остались ли локальные копии удалённых веток:
на QA-стенде (вдруг туда накатывался конкретный коммит),
у тестировщиков (бывает, у них что-то закэшено в локальных репах).
Ответ — нет. Никто не держит эти ветки у себя, и на стенд накатывали уже собранный релиз. Следов не осталось.
Следующий ход — найти разработчика, который делал задачи. Возможно, он не успел удалить у себя локальную ветку, даже если она была удалена из репозитория.
Я проверяю MR и вижу, кто автор. Минус: он в отпуске.
Нехорошо тревожить по пустякам (хотя это не пустяк, конечно).
К счастью, над задачей работали вдвоём. Пишу второму разработчику.
Ответ: «Я сейчас у врача, смогу посмотреть через пару часов».
На этом моменте я ставлю вторую попытку на паузу и переключаюсь на оставшиеся части релиза.
Остаётся последний шанс: Merge Request в GitLab.
Да, ветки удалены. Да, в Git их уже нет.
Но GitLab умеет сохранять сравнение веток, даже если сами ветки удалены.
Я открываю MR на удалённую ветку — и вижу:
diff между веткой и целевой (master) живой
все изменения, коммиты, комментарии — доступны
Это значит, что при желании, я могу вручную собрать эти коммиты, просто копируя diff кусками.
Это не идеально, но это шанс спасти релиз.
В это время поступает ответ от разработчика: одна из веток — сохранилась у него локально. Ура.
Но вторая — уже точно утрачена. Её нет ни у кого.
GitLab остаётся единственным источником для её восстановления.
Пока я разбирался с бэком, вспоминаю: есть ещё фронтовая релизная ветка.
К счастью, там я не делал reset --hard.
Но проблема с удалёнными ветками — та же. Задачи были смержены, ветки удалены, история отсутствует.
Решение:
вручную пересоздаю нужные ветки
поднимаю дифф из старых задач
cherry-pick'аю изменения обратно
одна из задач оказалась зависима от изменений в эпике (которые в другом релизе!) — пришлось вручную вырезать эту зависимость
Ещё одна задача попала в релиз недоделанной. Её убираю из релизной ветки и отправляю обратно на доработку.
В итоге мне удалось:
разделить фронт на два релиза
собрать релиз с «малыми» задачами
отложить эпик на следующий виток
На этом этапе часть задач по фронту уже спасена. Осталась проблема: бэковый релиз, в который я сделал git reset --hard и уронил всю историю. Напомню:
ветки с задачами были удалены сразу после мержа в релизную ветку;
один разработчик был в отпуске, второй смог восстановить только одну задачу;
вторую ветку не удалось найти ни у кого — она ушла в небытие.
Проверяю Merge Request, который когда-то мержил вторую задачу.
Сами ветки уже удалены, но GitLab по-прежнему показывает:
diff между удалённой веткой и master,
все изменённые файлы,
старый коммит с идентификатором,
комментарии к MR.
Это значит, что все изменения технически доступны, пусть и не в виде ветки.
Вариантов немного:
Создаю новую ветку от master: git checkout -b restore-lost-task
Перехожу в MR, открываю вкладку "Changes".
Файл за файлом, правка за правкой — воссоздаю изменения вручную:
копирую код из GitLab,
вставляю в IDE,
коммитю по логике задачи.
Да, это долго. Да, это неудобно.
Но когда речь идёт о продакшен-релизе, у которого сегодня дедлайн, — считаешь каждую строчку.
После ручной реконструкции:
проверяю локально, что поведение корректно;
прокидываю ветку в релизную;
уведомляю команду и QA: задача восстановлена, можно тестировать.
Спустя несколько итераций и сверок, релиз бэка собран заново — уже без потерь.
Этот релиз стал для меня хорошим напоминанием:
🔥 git reset --hard на релизной ветке — очень плохая идея, особенно если ветки уже удалены.
🔒 Удаление веток после мержа — удобно, но опасно, если прод ещё не обновился.
🧰 GitLab — это не только CI/CD, но и ценнейшее хранилище истории:
diff’ы остаются даже после удаления веток;
коммиты видны;
можно восстановить почти всё руками.
💬 Не стоит стесняться пинговать коллег, даже если они «в отпуске» — но лучше иметь бэкапы по процессу.
Мне интересно — как бы вы поступили в такой ситуации?
Можно ли было восстановить ветку через reflog, gitlab API или ещё как-то, о чём я не знал?
Может, вы используете другие подходы к управлению релизными ветками?
Стоит ли автоматизировать часть процессов (например, авто-бэкап перед reset / merge)?
Если у вас есть опыт с подобными граблями — напишите, будет круто сравнить.
Автор: igoryan20
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/git/423301
Ссылки в тексте:
[1] Источник: https://habr.com/ru/articles/920778/?utm_source=habrahabr&utm_medium=rss&utm_campaign=920778
Нажмите здесь для печати.