Руководство по исправлению ошибок с помощью Git (часть 2)
Опубликовано: 2022-03-10Во второй части нашей серии статей «Как исправить ошибки с помощью Git» мы снова смело посмотрим в глаза опасности: я подготовил четыре новых сценария конца света, включая, конечно же, несколько умных способов спасти наши шеи! Но прежде чем мы углубимся: ознакомьтесь с предыдущими статьями о Git, чтобы узнать о других методах самоспасения, которые помогут вам исправить свои ошибки с помощью Git!
Поехали!
Восстановление удаленной ветки с помощью Reflog
Вы когда-нибудь удаляли ветку и вскоре после этого понимали, что этого делать не следовало? В том маловероятном случае, если вам не знакомо это чувство, я могу сказать вам, что оно нехорошее. Смесь печали и гнева подкрадывается к вам, когда вы думаете обо всей тяжелой работе, проделанной для коммитов этой ветки, обо всем ценном коде, который вы теперь потеряли.
К счастью, есть способ вернуть эту ветку из мертвых — с помощью инструмента Git под названием «Reflog». Мы использовали этот инструмент в первой части нашей серии, но вот небольшое напоминание: Reflog похож на журнал, в котором Git отмечает каждое движение указателя HEAD в вашем локальном репозитории. Другими словами, менее заумные слова: каждый раз, когда вы извлекаете, фиксируете, объединяете, перебазируете, выбираете вишни и т. д., создается запись в журнале. Это делает Reflog идеальной подстраховкой, когда что-то идет не так!
Давайте рассмотрим конкретный пример:
$ git branch * feature/login master
Мы видим, что в настоящее время у нас проверена feature/login
нашей ветки. Предположим, что это ветка, которую мы собираемся удалить (непреднамеренно). Однако, прежде чем мы сможем это сделать, нам нужно переключиться на другую ветку, потому что мы не можем удалить нашу текущую ветку HEAD!
$ git checkout master $ git branch -d feature/login
Наша ветка с ценными функциями исчезла, и я дам вам минуту, чтобы (а) понять серьезность нашей ошибки и (б) немного пооплакать. После того, как вы вытерли слезы, нам нужно найти способ вернуть эту ветку! Давайте откроем Reflog (просто набрав git reflog
) и посмотрим, что он нам приготовил:
Вот несколько комментариев, которые помогут вам разобраться в выводе:
- Прежде всего, вам нужно знать, что Reflog сортирует свои записи в хронологическом порядке: самые новые элементы находятся вверху списка.
- Самый верхний (и, следовательно, самый новый) элемент — это команда
git checkout
, которую мы выполнили перед удалением ветки. Это регистрируется здесь в Reflog, потому что это одно из тех «движений указателя HEAD», которые Reflog так прилежно записывает. - Чтобы исправить нашу серьезную ошибку, мы можем просто вернуться в состояние до этого — что также чисто и ясно записано в Reflog!
Итак, давайте попробуем это, создав новую ветку (с именем нашей «потерянной» ветки), которая начинается с этого хэша SHA-1 состояния «до»:
$ git branch feature/login 776f8ca
И вуаля! Вы будете рады видеть, что мы восстановили нашу, казалось бы, потерянную ветку!
Если вы используете графический интерфейс Git для рабочего стола, такой как «Tower», вы можете воспользоваться хорошим ярлыком: просто нажмите CMD + Z на клавиатуре, чтобы отменить последнюю команду — даже если вы только что насильственно удалили ветку!
Перемещение коммита в другую ветку
Во многих командах существует соглашение не фиксировать долго выполняющиеся ветки, такие как main
или develop
: такие ветки должны получать новые коммиты только через интеграцию (например, слияния или перебазирования). И все же, конечно, ошибки неизбежны: мы иногда забываем и тем не менее совершаем эти ветки! Итак, как мы можем убрать беспорядок, который мы сделали?
К счастью, такие проблемы можно легко исправить. Засучим рукава и приступим к работе.
Первый шаг — переключиться на правильную ветку назначения, а затем переместить фиксацию, используя команду cherry-pick
:
$ git checkout feature/login $ git cherry-pick 776f8caf
Теперь у вас будет фиксация в нужной ветке, где она должна была быть изначально. Потрясающий!
Но осталось сделать еще одно: нам нужно сначала очистить ветку, на которую он случайно приземлился! Команда cherry-pick
, так сказать, создала копию коммита, но оригинал все еще присутствует в нашей долго работающей ветке:
Это означает, что мы должны вернуться к нашей долго работающей ветке и использовать git reset
для ее удаления:
$ git checkout main $ git reset --hard HEAD~1
Как видите, здесь мы используем команду git reset
, чтобы стереть ошибочный коммит. Параметр HEAD~1
указывает Git «вернуться на 1 ревизию назад за HEAD», эффективно удаляя самую верхнюю (и в нашем случае нежелательную) фиксацию из истории этой ветки.
И вуаля: коммит теперь там, где он должен был быть в первую очередь, и наша длительная ветка чиста — как будто нашей ошибки никогда не было!
Редактирование сообщения старого коммита
Слишком легко вставить опечатку в сообщение коммита и обнаружить ее гораздо позже. В таком случае старый добрый --amend
параметр git commit
не может решить эту проблему, потому что он работает только для самого последнего коммита. Чтобы исправить любую фиксацию, которая старше этой, мы должны прибегнуть к инструменту Git под названием «Интерактивная перебазировка».
Во-первых, мы должны сообщить Interactive Rebase, какую часть истории коммитов мы хотим отредактировать. Это делается путем передачи ему хэша коммита: родительского коммита того, которым мы хотим манипулировать.
$ git rebase -i 6bcf266b
После этого откроется окно редактора. Он содержит список всех коммитов после того, который мы предоставили в качестве основы для Interactive Rebase в команде:
Здесь важно, чтобы вы не следовали своему первому импульсу: на этом шаге мы еще не редактируем сообщение коммита. Вместо этого мы только сообщаем Git, какие манипуляции мы хотим выполнить с какими коммитами. Весьма удобно, что в комментариях внизу этого окна есть список ключевых слов действия. В нашем случае мы размечаем строку #1 с помощью reword
(тем самым заменяя стандартный pick
).
Все, что осталось сделать на этом шаге, это сохранить и закрыть окно редактора. В ответ откроется новое окно редактора, содержащее текущее сообщение фиксации, которую мы разметили. И вот , наконец, пришло время внести свои правки!
Вот весь процесс с первого взгляда для вас:
Исправление сломанного коммита (очень элегантным способом)
Наконец, мы собираемся взглянуть на fixup
, швейцарский армейский нож среди инструментов для удаления. Проще говоря, это позволяет вам исправить сломанный/неполный/неправильный коммит постфактум. Это действительно замечательный инструмент по двум причинам:
- Неважно, в чем проблема.
Возможно, вы забыли добавить файл, должны были что-то удалить, сделать неправильное изменение или просто опечатку.fixup
работает во всех этих ситуациях! - Это очень элегантно.
Наша нормальная инстинктивная реакция на ошибку в коммите — создать новый коммит, исправляющий проблему. Такой способ работы, каким бы интуитивным он ни казался, очень быстро делает историю коммитов очень хаотичной. У вас есть «оригинальные» коммиты, а затем эти маленькие «вспомогательные» коммиты, которые исправляют то, что пошло не так в исходных коммитах. Ваша история усеяна мелкими, бессмысленными коммитами, из-за которых трудно понять, что произошло в вашей кодовой базе.
Вот здесь-то и вступает в дело исправление. Оно позволяет вам по-прежнему делать эту корректирующую fixup
фиксацию. Но тут начинается волшебство: затем он применяет его к исходному, сломанному коммиту (исправляя его таким образом), а затем полностью отбрасывает уродливый пластырь!
Мы можем пройти практический пример вместе! Допустим, выбранный здесь коммит не работает.
Допустим также , что я подготовил изменения в файле с именем error.html
, которые решат проблему. Вот первый шаг, который нам нужно сделать:
$ git add error.html $ git commit --fixup 2b504bee
Мы создаем новый коммит, но сообщаем Git, что он особенный: это исправление старого коммита с указанным хэшем SHA-1 (в данном случае 2b504bee
).
Теперь вторым шагом является запуск сеанса Interactive Rebase, поскольку fixup
принадлежит большому набору инструментов Interactive Rebase.
$ git rebase -i --autosquash 0023cddd
Об этой команде стоит пояснить две вещи. Во-первых, почему я указал здесь 0023cddd
в качестве хэша ревизии? Потому что нам нужно начать нашу сессию Interactive Rebase с родительской фиксации нашего сломанного товарища.
Во-вторых, для чего нужна опция --autosquash
? С наших плеч уходит много работы! В открывшемся окне редактора для нас уже все подготовлено:
Благодаря опции --autosquash
Git уже сделал за нас всю тяжелую работу:
- Он пометил нашу маленькую временную фиксацию ключевым словом
fixup
action. Таким образом, Git объединит его с фиксацией непосредственно выше , а затем отбросит его. - Он также соответствующим образом переупорядочил строки, переместив наш вспомогательный коммит прямо под коммит, который мы хотим исправить (опять же:
fixup
работает, комбинируя размеченный коммит с коммитом выше !).
Короче: нам ничего не остается, как закрыть окно!
Давайте в последний раз посмотрим на конечный результат.
- Бывший неработающий коммит исправлен: теперь он содержит изменения, которые мы подготовили в нашем пластыре коммита.
- От самого уродливого пластыря-фиксатора отказались: история коммитов чистая и легко читаемая — как будто и не было никакой ошибки.
Умение исправлять ошибки — это суперсила
Поздравляем! Теперь вы можете спасти свою шею во многих сложных ситуациях! На самом деле мы не можем избежать этих ситуаций: независимо от того, насколько мы опытны как разработчики, ошибки — это просто часть работы. Но теперь, когда вы знаете, как с ними справляться, вы можете встречать их с непринужденным сердечным ритмом.
Если вы хотите узнать больше об исправлении ошибок с помощью Git, я могу порекомендовать бесплатную «Аптечку первой помощи для Git», серию коротких видеороликов именно на эту тему.
Получайте удовольствие от ошибок — и, конечно же, с легкостью их исправляйте!