урок 4 из 10

История и откат

Читать историю — log, show, diff между коммитами, blame — и выбирать откат по тому, где лежит правка: restore для несохранённого, revert для отправленного, reset и --amend только для своего неотправленного.

8 мин чтения · 4 главы ·6 заданий ·домашняя работа

Читать историю

История нужна, чтобы её читать. Вот основные способы.

git log --oneline          # коротко: идентификатор и первая строка
git log                    # подробно: автор, дата, сообщение целиком
git log -p                 # с изменениями каждого коммита
git log -- index.html      # только коммиты, менявшие этот файл
git show c92f3a1           # один коммит: сообщение и дифф
git diff b5e0d44 d41e7b0   # разница между двумя коммитами

Самые полезные на практике — история одного файла и разница между двумя коммитами. Первая отвечает на вопрос «кто и когда трогал этот файл», вторая — «что изменилось между вчерашней рабочей версией и сегодняшней сломанной».

Ещё один вопрос, который Git решает за секунды, — кто написал эту строку. Команда git blame с именем файла показывает у каждой строки коммит, автора и дату последнего изменения. Название пугающее («обвинить»), но смысл мирный: найти коммит, а по нему — сообщение с объяснением, зачем строка такая.

Многие редакторы и сервисы показывают то же самое кнопками: историю файла, автора строки, разницу версий. За ними стоят те же команды — и тот же вывод.

Несохранённое: restore

Откат начинается с вопроса: где лежит то, что нужно отменить? От ответа зависит команда.

Правка нев коммите:restoreКоммит ужеотправлен:revertКоммит толькоу вас:reset
Главный вопрос — где лежит то, что нужно отменить. Не сохранено — restore. Сохранено и уже у коллег — revert. Сохранено только у вас — можно reset.

Правка ещё не в коммите. Вы что-то поменяли в файле и хотите вернуть его к последнему сохранённому состоянию:

git restore index.html            # вернуть файл к последнему коммиту
git restore --staged index.html   # убрать из индекса, правку оставить

Первая команда выбрасывает несохранённые правки в файле. Вторая — мягкая: она только вынимает файл из индекса, а сами правки остаются в рабочей папке. Её используют, когда случайно добавили не то.

В старых инструкциях вместо git restore встречается git checkout -- файл: это то же самое, просто прежняя форма команды. У checkout много разных обязанностей, поэтому для понятности их разнесли по новым командам — restore для файлов и switch для веток.

Отправленное: revert

Правка уже в коммите, и коммит отправлен — у коллег он есть. Здесь действует второе правило курса: общую историю не переписывают. Инструмент для такого отката — git revert.

git revert c92f3a1

Команда создаёт новый коммит, который делает изменение, обратное указанному: добавленные строки удаляет, удалённые возвращает. Старый коммит остаётся в истории, а сверху появляется его отмена.

mainABCDRошибкаHEAD
Коммит C оказался ошибкой. git revert создаёт новый коммит R, который делает обратное изменение. История не переписана: C остаётся в ней, и у коллег ничего не ломается.

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

Если отменяемый коммит затрагивает строки, которые потом меняли ещё раз, revert может остановиться на конфликте — Git не сможет сам решить, как отменить. Как разбирать конфликты, вы узнаете в шестом уроке; принцип тот же.

А если нужно просто посмотреть, как проект выглядел раньше, откатывать ничего не надо. Команда git switch --detach с идентификатором коммита переносит рабочую папку в прошлое «на посмотреть»: можно запустить старую версию и сравнить. Вернуться — git switch main. Git при этом предупредит о «detached HEAD»: это значит, что вы не в ветке, и новые коммиты здесь легко потерять. Подробнее — в уроке 10.

Своё неотправленное: reset

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

git reset --soft HEAD~1   # убрать последний коммит, правки оставить в индексе
git reset HEAD~1          # то же, правки оставить в рабочей папке
git reset --hard HEAD~1   # убрать коммит и выбросить его правки

HEAD~1 значит «на один коммит назад от текущего». Три режима отличаются тем, что происходит с изменениями из убранного коммита:

РежимКоммитПравки
--softубранв индексе, готовы к новому коммиту
без ключаубранв рабочей папке
--hardубранвыброшены

Мягкие режимы удобны, чтобы переделать коммит: разбить его на два, поправить сообщение, добавить забытый файл. Для последнего есть и короткий путь — git commit --amend: он заменяет последний коммит новым, включив в него то, что сейчас в индексе, и позволяет поправить сообщение.

Коротко о выборе: несохранённое — restore; отправленное — revert; своё неотправленное — reset или --amend. Если сомневаетесь, отправлен ли коммит, — считайте, что отправлен, и берите revert: он безопасен всегда.

Задания урока

Первые задание решается прямо здесь — без регистрации. Щёлкните по ответу.

  1. 1. Какая команда покажет, кто последним менял каждую строку файла?

Остальные задания урока — в курсе, с сохранением прогресса и разбором ошибок.

  1. Расставьте поиск причины: вчера работало, сегодня нет.средне
  2. Сопоставьте, где лежит правка, и команду отката.легко
  3. git revert удаляет ошибочный коммит из истории.средне
  4. Заполните пропуски.средне
  5. Разложите команды: можно вернуть правки после неё или нет.средне

Практика и материалы по теме курса

Пройти урок с проверкой заданий Курс «Git и работа в команде» открыт и бесплатен, прогресс сохраняется.
Открыть курс