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

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

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

В первой статье мы рассмотрим различные ошибки и способы их исправления с помощью Git!

Отмена незафиксированных изменений в файле

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

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

Узнав это небольшое предупреждение, давайте отменим наши изменения в index.html:

git restore index.html

Эта команда восстановит наш файл до последнего зафиксированного состояния, очистив его от любых локальных изменений.

Восстановления удалённых файлов

Давайте с предыдущим примером пойдём немного дальше. Допустим вы удалили index.html, а не изменили его. Опять же, предположим, то вы ещё не внесли изменения в репозиторий.

Вам приятно будет узнать, что git restore так же легко справится с этой ситуацией:

git restore index.html

Команде restore всё равно что вы сделали с этим плохим файлом. Она просто воссоздаст своё последнее зафиксированное состояние.

Отмена некоторых изменений

Большинство дней — смесь хорошей и плохой работы. А иногда у нас бывает и то и другое в одном файле: Некоторые из ваших модификаций будут отличными (давайте проявим щедрость и назовём их гениальными), а другие подходят для мусорной корзины.

Git позволяет детально работать с изменениями. Использование git restore с флагом -p делает процедуру отмены более тонкой:

git restore -p index.html

Git берёт нас за руку и проводит по каждому фрагменту изменений в файле, спрашивая, хотим ли вы удалить его (в этом случае нужно нажать y) или оставить (нажав n):

Если вы используете приложение для работы с Git, вы можете пойти ещё дальше. Подобные приложения позволяют вам выбирать, какой код сохранить, удалить и обработать не только на уровне фрагментов, но и для отдельных строк. Один из таких инструментов Tower, над которым работает ваш покорный слуга.

Исправление последнего коммита/фиксации

Поднимите руку, если вы никогда не делал опечаток в сообщении коммита или не забывали добавить последнее изменение. Нет поднятых рук? Это то, о чём я думал. Поскольку опечатка в сообщении коммита распространённое явление, Git позволяет очень легко исправлять такие ошибки.

Опция --amend позволяет изменить последний коммит (и только его):

git commit --amend -m "A message without typos"

Если вы забыли добавить определённое изменение, вы легко можете сделать это. Просто добавьте его как и любое другое изменение, с помощью команды git add, а затем снова запустите git commit --amend:

git add forgotten-change.txt

git commit --amend --no-edit

Опция --no-edit сообщает Git, что вы не хотите изменять сообщение коммита.

Отмена последствий плохого коммита

В вышеперечисленных случаях мы быстро осознавали свои ошибки. Но часто мы узнаём об ошибке только спустя долгое время после её совершения. Плохой коммит сидит в истории изменений язвительно глядя на нас.

Плохой коммит

Конечно, есть решение и этой проблемы: команда git revert! И это решает нашу проблему не разрушительным способом. Вместо того чтобы удалить наш плохой коммит из истории, он создаст новый, с противоположными изменениями.

git revert

Выполнить это в командной строке так же просто, как предоставить хэш исправления плохого коммита в git revert.

git revert 2b504bee

Как уже упоминалось выше, это не удалит плохой коммит (что может быть проблематичным, если мы уже поделились им с коллегами в удалённом репозитории). Вместо этого будет автоматически создан новый коммит, содержащий отменённые изменения.

Восстановление предыдущего состояния проекта

Иногда приходится признать, что мы зашли в тупик. Возможно, пара последних коммитов не принесла никаких результатов, и лучше их отменить.

Восстановление предыдущего состояния

К счастью, решить эту проблему довольно просто. Просто нужно предоставить хэш SHA-1 ревизии, к которой хотим вернуться, команде git reset. Любые коммиту сделанные после этой ревизии исчезнут из истории.

git reset --hard 2b504bee

Параметр --hard гарантирует, что у нас останется чистая рабочая копия. В качестве альтернативы мы можем использовать параметр --mixed для большей гибкости (и безопасности): --mixed сохранит изменения, которые содержались в удалённых коммитах, как локальные изменения в нашей рабочей копии.

Восстановление утерянных коммитов с reflog

Вы уже заметили, что когда дело доходит до исправления ошибок, с Git возможно практически всё. Включая отмену отмены. Допустим, мы поняли, что git reset, который мы выполняли выше, был не самой лучшей идеей.

Как вы догадались, эта проблема решается определённым инструментом. reflog — своего рода журнал, где Git протоколирует перемещения указателя HEAD. Другими словами, каждый раз, когда мы фиксируем изменения, проверяем, объединяем, перебазируем и т.д. В этом журнале создаётся новая запись. К счастью, это же происходит, когда мы используем git reset!

Давайте откроем reflog командой git reflog. Взгляните, что у нас есть:

Восстановление утерянных коммитов

Первое, что нужно знать о reflog — это то, что он упорядочен в хронологическом порядке. Поэтому неудивительно, что наша недавняя ошибка git reset находится в самом верху. Если мы хотим отменить её, мы можем вернуть предыдущее состояние, которое также запротоколировано здесь, ниже!

Мы можем скопировать хэш коммита этого безопасного состояния и создать на его основе новую ветку:

git branch happy-ending e5b19e4

Конечно, мы могли использовать git reset e5b19e4 для возврата в предыдущее состояние. Однако я предпочитаю создавать новую ветку: она не имеет недостатков и позволяет проверить, действительно ли это то состояние, которое мне нужно.

Восстановление одного файла из предыдущего состояния

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

  1. найти коммит в котором мы удалили файл
  2. потом (и только потом) восстановить его.

Поищем в истории коммитов наш утерянный файл:

git log -- <filename>

Результатом будет список всех коммитов, в которых этот файл изменялся. А поскольку журнал отсортирован в хронологическом порядке — коммит в котором мы удалили файл будет первым (так как после удаления файла, он не будет отображаться в новых коммитах).

В хэшем коммита и именем файла у нас есть всё необходимое для его восстановления.

git checkout <deletion commit hash>~1 -- <filename>

Обратите внимание, что мы используем ~1 для обращения к коммиту перед тем в котором мы его удалили. Это необходимо, поскольку коммит, в котором произошло удаление, больше не содержит файл. Поэтому мы не можем использовать его для восстановления файла.

Вы (почти) непобедимы

Читая эту статью мы стали свидетелями множества катастроф, но убедились, что в Git практически всё можно исправить! Узнав правильные команды, вы всегда сможете найти способ спасти положение.

Но чтобы стать действительно непобедимым (всего лишь в Git), вам необходимо дождаться второй части из этой серии. Мы рассмотрим ещё несколько сложных проблем, например, восстановление удалённой ветки, перемещение коммитов между ветками и как объединить несколько коммитов в один!

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

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

Рассмотрим Tailwind CSS

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

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