Меню Закрыть

Git вернуться к последнему коммиту

Содержание

Если я чему-то научился за 15 с лишним лет программирования, так это то, что ошибки встречаются часто, и я их много делаю. Это в равной степени относится и к инструментам контроля версий. Независимо от того, случайно ли вы зафиксировали изменения или просто поняли, что ваш предыдущий зафиксированный код — это не то, что вам нужно, часто вам потребуется отменить предыдущий коммит в Git.

В этой статье я покажу несколько способов отменить ваши коммиты, в зависимости от вашего варианта использования. Это сложная тема (которая относится ко многим темам Git в целом), поэтому убедитесь, что вы следуете инструкциям, которые лучше всего соответствуют вашим потребностям.

Удалить неопубликованные коммиты

Если вы еще не опубликовали свои коммиты в удаленном репозитории, таком как GitHub, вы можете по существу удалить предыдущие коммиты с помощью команды reset.

Хотя это эффективное решение, оно опасное, поскольку вы переписываете историю и оставляете «удаленные» коммиты без ссылок или «осиротевшими». Единственный способ найти и восстановить эти несвязанные коммиты — это git reflog.

Команда reset имеет три различных параметра, два из которых мы опишем здесь:

Используя опцию —hard, все возвращается обратно к указанному коммиту. Это включает в себя ссылки на историю коммитов, промежуточный индекс и ваш рабочий каталог.

Это означает, что с помощью этой команды вы не только вернетесь к предыдущей фиксации, но и потеряете все рабочие изменения в процессе. Чтобы не потерять какие-либо рабочие изменения, вы можете использовать команды stash и stash pop:

Команда stash сохраняет ваши рабочие изменения (без каких-либо комитов или изменений в дереве), а затем stash pop возвращает их обратно.

Другим вариантом, который вы можете рассмотреть, является параметр —soft. Эта опция работает так же, как git reset —hard , но влияет только на историю коммитов, а не на ваш рабочий каталог или промежуточный индекс.

Удаление опубликованных коммитов

Допустим, вы зафиксировали свой код, а затем отправили его в удаленный репозиторий. На этом этапе настоятельно рекомендуется не использовать что-то вроде git reset, поскольку вы переписываете историю.

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

Допустим, в вашем репозитории есть текстовый файл со следующим содержанием

И вы изменяете его на:

Ваша история коммитов может выглядеть примерно так:

Если мы решили, что нам больше ненужно слово «awesome» в нашем тексте, но мы не хотим удалять коммит 676ec, мы можем использовать revert, чтобы отменить это изменение:

Получив приглашение ввести сообщение о коммите, мы теперь можем видеть в нашей истории коммитов, что фактически существует новый коммит:

В результате этого первый и третий коммиты представляют одно и то же состояние проекта. Коммит был отменен, и история не была потеряна.

Обратите внимание, что есть несколько других способов использовать эту команду, например, если вы хотите вернуть обратно 2 коммита, вы можете использовать:

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

Временно оформить предыдущий коммит

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

Читайте также:  Arctic cooling f14 silent

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

Я хочу вернуться к более раннему коммиту. Как мне это сделать?

Вот что показывает команда git log :

1 ответ 1

Этот вопрос можно понять по-разному:

  1. Что значит вернуться или откатиться: просто посмотреть, изменить содержимое рабочей области, изменить историю Git?
  2. Что именно откатить: рабочую область (worktree), индекс (область подготовки коммита, staging area), текущую ветку, удаленную ветку?
  3. К какой позиции откатить: к индексу, к последнему коммиту, к произвольному коммиту?

Обозначим начальную ситуацию на следующей схеме:

A , B , C , D — коммиты в ветке master .
(HEAD) — местоположение указателя HEAD.
(i) — состояние индекса Git. Если совпадает c (HEAD) — пуст. Если нет — содержит изменения, подготовленные к следующему коммиту.
(wt) — состояние рабочей области проекта (working tree). Если совпадает с (i) — нет неиндексированных изменений, если не совпадает — есть изменения.
↑ обозначает коммит, на который указывает определенная ветка или указатель.

Вот решения, в зависимости от задачи:

Если вам нужно просто переключиться на другой коммит, чтобы, например, посмотреть на его содержимое, достаточно команды git checkout :

Сейчас репозиторий находится в состоянии «detached HEAD». Чтобы переключиться обратно, используйте имя ветки (например, master ):

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

3.1 Безопасно — с помощью кармана (stash)

3.1.1 Только неиндексированные

Можно удалить прикарманить только те изменения, которые еще не были индексированы (командой add ):

3.1.2 Индексированные и нет

Эта команда отменяет все индексированные и неиндексированные изменения в рабочей области, сохраняя их в карман (stash).

Восстановление несохраненных изменений: легко и просто.

Если stash совсем не нужен, его можно удалить.

После этого восстановить изменения всё ещё можно, но сложнее: How to recover a dropped stash in Git?

3.2 Опасный способ

Осторожно! Эта команда безвозвратно удаляет несохраненные текущие изменения из рабочей области и из индекса Если они вам все-таки нужны, воспользуйтесь git stash .

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

Здесь мы будем использовать git reset —hard

Осторожно! Эта команда переписывает историю Git-репозитория. Если вы уже опубликовали ( git push ) свои изменения, то этот способ использовать нельзя (см. почему). Используйте вариант из пункта 5 ( git revert ).

4.1 При этом сохранить изменения в индекс репозитория:

После этого индекс репозитория будет содержать все изменения от cccccc до dddddd . Теперь вы можете сделать новый коммит (или несколько) на основе этих изменений.

4.2 Сохранить изменения в рабочей области, но не в индексе.

Эта команда просто перемещает указатель ветки, но не отражает изменения в индексе (он будет пустым).

4.3 Просто выбросить изменения.

Осторожно! Эта команда безвозвратно удаляет несохраненные текущие изменения. Если удаляемые коммиты не принадлежат никакой другой ветке, то они тоже будут потеряны.

Восстановление коммитов: Используйте git reflog и этот вопрос чтобы найти и восстановить коммиты; иначе сборщик мусора удалит их безвозвратно через некоторое время.

Читайте также:  Sqlgrammarexception could not extract resultset

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

Воспользуйтесь командой git revert . Она создает новые коммиты, по одному на каждый отменяемый коммит. Таким образом, если нужно отменить все коммиты после aaaaaa :

Восстановление: Если revert-коммит оказался ошибочным, используйте этот ответ.

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

В этой статье я хочу рассказать о командах git checkout и git reset с ключами —soft и —hard .

Итак, начнём краткий ликбез по машине времени, предоставляемой git’ом. Сперва проиллюстрируем историю:

Здесь кружочками обозначены коммиты. Чем правее коммит, тем он новее. Коммит с хэшем 6e04e..-это самый первый коммит. Одно из основных понятий, которое стоит уяснить себе новичку, — это указатели на коммиты, а точнее некоторое «прозвище» того или иного коммита. Их тьма тьмущая, например: HEAD, master, FETCH_HEAD, ORIG_HEAD и т.д. Это я перечислил крупицу стандартных прозвищ. Их можно создавать и самим, но об этом впереди.

Заострим наше внимание на двух указателях: master и HEAD. master указывает на самый старший коммит в ветке под названием master (эта ветка создаётся при инициализации репозитория). HEAD указывает на указатель master (читай, текущее состояние файлов). После появления первого коммита в репозитории, HEAD и master указывают на один и тот же коммит. И так будет продолжать до тех пор, пока не переключимся на другую ветку, не откатимся по истории, либо не совершим ряд необдуманных действий. Итак, проиллюстрируем нашу историю с указателями:

Указатель HEAD в нашем случае указывает на master, а master — на коммит d79fb… Архиважно понять, что текущее состояние неизменённых файлов, находящихся под контролем версий, есть тот коммит, на который указывает HEAD. То есть, если HEAD будет указывать на коммит с хэшем 6e04e. то файлы окажутся в первоначальном своём состоянии. Для «движения» указателя HEAD существует команда: git checkout . Те, кто знаком хоть чуть-чуть с git’ом, узнали в этой команде переключение на другую ветку. Всё совершенно верно — при переключении на другую ветку мы просто переносим указатель HEAD на последний коммит ветки.

Перенос указателя HEAD ( git checkout )

Откат по истории коммитов:

После завершения операции checkout мы будем находиться в состоянии, в котором были два коммита назад. Это всё прекрасно — мы сделали шажок в прошлое, что-то там подглядели, но как вернуться назад? Я вот, например, не обладаю сверхпамятью, и не помню хэш самого последнего коммита (тот, который самый правый — d79fb..). Если написать git log , то увидим историю, состоящую из трёх коммитов:

Неужели мы потеряли всю историю? Как узнать самый «новый» коммит? Это не проблема — есть выход, и их несколько:

    Написать команду git log —all . Данная команда напечатает нам всю историю, вплоть до современности, т.е. в нашем случае историю из пяти коммитов:
Читайте также:  Intel r xeon r cpu e5 1620

Далее остаётся скопировать нужный нам хэш и вновь запустить машину времени: git checkout . Но данный способ не рекомендую, так как он требует слишком много действий.

  • Git позволяет отслеживать все изменения указателя HEAD. Это возможно командой git reflog , но это уже не для новичков и используется не для поставленных нами целей. Самое грамотное — это поступить следующим образом:
  • Вспомнить, что указатель master указывает на самый свеженький коммит. Таким образом, возврат в исходное состояние выполняется одной командой: git checkout master . Вуа-ля:
  • Для прояснения механизма git checkout создадим новую ветку devel:

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

    Заметим, что указатель HEAD указывает на вершину ветки devel.

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

    Возвращение в ветку master происходит также безболезненно:

    Итак, запоминаем первый пункт:

    • Комнда git checkout передвигает указатель HEAD

    Перенос указателя на вершину ветки ( git reset . )

    Кроме того, git позволяет двигать не только HEAD, но и континеты указатели на вершины веток. Для этого существует команда git reset с ключами либо —soft , либо —hard .

    • Ключ —hard означает, что мы теряем текущее состояние файлов и приобретаем состояние того коммита, куда был сделан reset.
    • Ключ —soft означает, что мы НЕ теряем текущее состояние проекта, но указатель на текущую ветку уже передвинут, т.е. git status нам выдаст разницу между текущим состоянием проекта (от которого мы сделали reset) и тем, на который мы сделали reset.

    В обоих случаях появляется «прозвище» для коммита, с которого был совершён reset — ORIG_HEAD.

    git reset —hard HEAD

    2 :

    git reset —soft HEAD

    2 :

    ORIG_HEAD полезен для редактирования неверных коммитов на локальной машине (!). Предположим, что мы хотим объединить два последних коммита в единый. Для этого, сохраняя текущее состояние файлов, переводим указатель master на два коммита назад:

    Посмотрим на изменения:

    Ну а теперь сделаем трюк — объединяем коммиты

    Вводим сообщение, сохраняемся. Теперь наша история выглядит вот так:

    Важное замечание — ORIG_HEAD по-прежнему указывает на коммит d79fb… Если мы сейчас выполним команду git checkout ORIG_HEAD, то мы получим так называемое состояние detach HEAD. Оно характеризуется тем, что HEAD указывает не на вершину ветки, а просто на коммит. HEAD всегда должен указывать только на вершину какой-либо ветки!

    Чтобы «выйти» из состояния detach HEAD достаточно просто переключиться на какую-либо ветку или создать новую ветку командой git checkout -b new_branch_name
    Итак, запоминаем второй пункт:

    • git reset с ключами —soft или —hard двигает указатель на вершину ветки, а вместе с ним и указатель HEAD.

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

    Удачных вам путешествий по истории своего репозитория!

    При подготовке материала использовались следующие источники:
    Самый лучший manual — книга: ProGit
    Наглядная справка по git: A Visual Git Reference (Русская версия)

    UPD:
    В комментариях посоветовали ещё один полезный ресурс по git`у: githowto

    Рекомендуем к прочтению

    Добавить комментарий

    Ваш адрес email не будет опубликован.