Git: Руководство по исправлению ошибок (Часть 2)

Источник: «A Guide To Undoing Mistakes With Git (Part 2)»
Ошибки. Эти жестокие злодеи не останавливаются даже на прекрасном мире разработки программного обеспечения. Мы не можем избежать ошибок, но мы можем научиться их исправлять! В этой статье будут рассмотрены инструменты, подходящие для повседневной работы с Git.

Во второй части нашей серии статей «Git: Руководство по исправлению ошибок» мы снова смело посмотрим опасности в глаза: я подготовил четыре новых сценария «Судного дня» — включая несколько умных способов спасти наши шеи! Но прежде чем мы начнём: ознакомьтесь с предыдущими статьями о Git, что бы узнать о дополнительных методах самопомощи, которые помогут вам исправить свои ошибки с помощью Git.

Восстановление удалённой ветви с помощью Reflog

Вы когда-нибудь удаляли ветку и понимали, что этого не стоило делать? В том маловероятном случае, если вы не знакомы с этим ощущением, могу сказать, что оно плохое. Смесь печали и гнева подкрадываются к вам, когда вы думаете о всей тяжёлой работе, которая была вложена в коммиты этой ветви. Обо всём ценном коде, который вы сейчас потеряли.

К счастью, есть способ вернуть эту ветвь из мёртвых — с помощью инструмента Git под названием Reflog. Мы использовали этот инструмент в первой части, но немного напомню: reflog похож на журнал, в котором Git отмечает каждое движение HEAD в вашем локальном репозитории. Другими словами: каждый раз когда вы выполняете checkout, commit, merge или rebase создаётся запись в журнале. Это делает reflog идеальной подстраховкой, если что-то пошло нет!

Рассмотрим конкретный пример.

$ git branch
* feature/login
master

Мы видим, что в настоящее время мы переключены на ветку feature/login. Допустим, это ветвь, которую мы собираемся удалить (случайно). Однако, прежде чем мы сможем это сделать, нам нужно переключиться на другую ветвь. Потому что мы не можем удалить ветвь на которую указывает HEAD.

git checkout master
git branch -d feature/login

Наша ветвь feature/login исчезла — я дам вам минуту, что бы (а) понять серьёзность нашей ошибки и (б) немного всплакнуть. После того как вы вытерли слёзы, нам нужно найти способ вернуть эту ветвь! Давайте откроем Reflog (просто набрав git reflog) и посмотрим, что он нам приготовил:

git reflog

Вот несколько комментариев, которые помогут разобраться вам в выводе:

Итак, давайте попробуем вернуть это состояние, создав новую ветвь (с именем нашей «потерянной» ветви), которое было «до» с хэшем SHA-1:

git branch feature/login 776f8ca

И вуаля! Вы будете счастливы, увидев, что мы восстановили, казалось бы, потерянную ветвь!

Если вы используете приложения для Git, такие, как Tower, вы можете воспользоваться сочетанием клавиш Ctrl + Z на клавиатуре, для отмены последней команды — даже если вы только что жестоко удалили ветвь репозитория!

Перенос коммита в другую ветвь

Во многих командах существует договорённость не делать коммитов в долгосрочные ветви, такие как main и develop: такие ветви должны получать новые коммиты только через интеграцию (merge или rebase). И всё же, конечно, ошибки неизбежны: мы иногда забываем и делаем коммиты в эти ветви! Итак, как мы можем навести порядок в созданном нами беспорядке?

Ошибочный коммит

К счастью, подобные проблемы легко исправить. Закатаем рукава и приступим к работе.

Первый шаг — переключиться на правильную ветвь назначения, а затем переместить коммит используя команду cherry-pick:

git checkout feature/login
git cherry-pick 776f8caf

Теперь у вас коммит в нужной ветви, где он и должен был оказаться. Потрясающе!

Но, осталось сделать ещё одно дело: нам нужно очистить ветвь, в которую он случайно попал в начале! Команда cherry-pick создала копию коммита, но оригинал всё ещё присутствует в долгосрочной ветви:

git cherry-pick

Это означает, что мы должны вернутся к нашей долгосрочной ветви и использовать git reset для удаления коммита:

git checkout main
git reset --hard HEAD~1

Как вы видите, мы использовали git reset для удаления ошибочного коммита. Параметр HEAD~1 указывает Git «вернутся на 1 ревизию за HEAD», эффективно удаляет самый верхний (и в нашем случае: не желательный) коммит из истории этой ветви.

И вуаля: коммит теперь там, где должен был быть в первую очередь, и наша долговременная вервь чиста - как будто нашей ошибки не было!

Редактирование сообщения старого коммита

Слишком легко пропустить опечатку в сообщении коммита — и обнаружить её намного позже. В таком случае старую добрую опцию git --amend нельзя использовать для решения проблемы, потому что она работает только для самого последнего коммита. Чтобы исправить более ранний коммит, мы должны прибегнуть к инструменту Git под названием «Интерактивное Перебазирования».

Список коммитов

Во-первых, мы должны сообщать «Interactive Rebase», какую часть истории коммитов хотим редактировать. Это делается путём передачи хэша коммита: родительского коммита, предка того коммита которым хотим манипулировать.

git rebase -i 6bcf266b

Откроется окно редактора. Оно содержит список всех коммитов, после того, который мы предоставили в качестве основы для интерактивного перебазирования в команде:

Interactive Rebase

Здесь важно, чтобы вы не последовали своему первому импульсу: на этом этапе мы ещё не редактируем сообщение коммита. Вместо этого мы только сообщаем Git, какие манипуляции хотим сделать и с какими коммитами. Очень удобно, что в комментариях внизу окна есть список ключевых слов для действий. В нашем случае мы помечаем строку №1 reword (тем самым заменяя стандартную pick)

Всё, что осталось сделать на этом шаге, — это сохранить и закрыть окно редактора. В свою очередь, откроется новое окно редактора, содержащее текущее сообщение отмеченного коммита. И, наконец, пришло время внести наши правки!

Вот вам краткий обзор всего процесса:

Исправление сломанного коммита (очень элегантным способом)

В конце, мы собираемся взглянуть на fixup, универсальный набор инструментов для отмены. Проще говоря, он позволяет вам исправить сломанный/неполный/неправильный коммит постфактум. Это действительно замечательный инструмент по двум причинам:

  1. Возможно, вы забыли добавить файл, должны были что-то удалить, внесли неправильную правку или просто опечатку. fixup работает во всех этих ситуациях.
  2. Наша нормальная инстинктивная реакция на ошибку в коммите — создание нового коммита для устранения проблемы. Такой способ, каким бы интуитивным он не казался, скоро сделает вашу историю коммитов хаотичной. У вас есть «оригинальные» коммиты, а затем эти небольшие «заплатки», которые исправляют то, что пошло не так в исходных коммитах. Ваша история усеяна небольшими бессмысленными коммитами, из-за которых сложно понять, что произошло в вашей кодовой базе.
Сломанные коммиты

Вот тут-то и пригодится fixup. Он позволяет вам по-прежнему делать эти исправляющие коммиты. Но здесь начинается волшебство: он применяет его к исходному, сломанному коммиту (исправляя его), а затем полностью удаляет уродливый коммит-заплатку!

fixup

Мы можем вместе разобрать практический пример! Допустим, выбранный коммит не работает.

Список коммитов

Предположим также, что я подготовил изменения в файле с именем error.html, которые решают проблему. Вот первый шаг, который нужно сделать:

git add error.html
git commit --fixup 2b504bee

Мы создаём новый коммит, но сообщаем Git, что это особенный коммит: это исправление для старого коммита с указанным SHA-1 хэшем (в нашем случае 2b504bee)

Второй шаг — начать сессию интерактивного перебазирования, потому что исправление относится к большому набору инструментов интерактивного перебазирования.

git rebase -i --autosquash 0023cddd

Об этой команде следует пояснить две вещи. Во-первых, почему я предоставил 0023cddd в качестве хэша ревизии? Потому что нам нужно начать сеанс интерактивного перебазирования с родительского коммита нашего сломанного коммита:

git rebase -i --autosquash

Благодаря опции --autosquash, Git уже сделал за нас тяжёлую работу:

  1. Он отметил наш небольшой временный коммит ключевым словом fixup. Таким образом, Git объединит его с приведённым выше коммитом, а затем отбросит его.
  2. Он также соответствующим образом изменил порядок строк, переместив наш временный коммит прямо перед коммитом, который мы хотим исправить (опять же: fixup работает путём объединения отмеченного коммита с предыдущим)

Короче: нам ничего не остаётся как закрыть окно редактора!

Давайте посмотрим на итоговый результат.

Чистая история коммитов

Знание как исправлять ошибки — сверхсила

Поздравляю! Теперь вы сможете спасти свою шею во многих сложных ситуациях! Мы не можем избежать этих ситуаций: независимо от того, насколько мы опытны как разработчики, ошибки - это просто часть работы. Но теперь, когда вы знаете как с ними бороться, вы можете столкнуться с ними не напрягаясь.

Получайте удовольствие от ошибок — и, конечно же, с лёгкостью исправляйте их!

Дополнительные материалы

Предыдущая Статья

Git: Руководство по исправлению ошибок (Часть 1)

Следующая Статья

Два умных способа организации структуры Sass