Как вернуться (откатиться) к более раннему коммиту?
Я хочу вернуться к более раннему коммиту. Как мне это сделать?
Вот что показывает команда git log :
Этот вопрос можно понять по-разному:
- Что значит вернуться или откатиться: просто посмотреть, изменить содержимое рабочей области, изменить историю Git?
- Что именно откатить: рабочую область (worktree), индекс (область подготовки коммита, staging area), текущую ветку, удаленную ветку?
- К какой позиции откатить: к индексу, к последнему коммиту, к произвольному коммиту?
Обозначим начальную ситуацию на следующей схеме:
A , B , C , D — коммиты в ветке master .
(HEAD) — местоположение указателя HEAD.
(i) — состояние индекса Git. Если совпадает c (HEAD) — пуст. Если нет — содержит изменения, подготовленные к следующему коммиту.
(wt) — состояние рабочей области проекта (working tree). Если совпадает с (i) — нет неиндексированных изменений, если не совпадает — есть изменения.
↑ обозначает коммит, на который указывает определенная ветка или указатель.
Вот решения, в зависимости от задачи:
1. Временно переключиться на другой коммит
Если вам нужно просто переключиться на другой коммит, чтобы, например, посмотреть на его содержимое, достаточно команды git checkout :
Сейчас репозиторий находится в состоянии «detached HEAD». Чтобы переключиться обратно, используйте имя ветки (например, master ):
2. Переключиться на коммит и продолжить работу с него
Если вы хотите продолжить работу с другого коммита, вам понадобится новая ветка. Можно переключиться и создать ее одной командой:
3. Удалить изменения в рабочей области и вернуть ее к состоянию как при последнем коммите.
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
4. Перейти к более раннему коммиту в текущей ветке и удалить из нее все последующие (неопубликованные)
Осторожно! Эта команда переписывает историю Git-репозитория. Если вы уже опубликовали ( git push ) свои изменения, то этот способ использовать нельзя (см. почему). Используйте вариант из пункта 5 ( git revert ).
4.1 При этом сохранить изменения в индекс репозитория:
После этого индекс репозитория будет содержать все изменения от cccccc до dddddd . Теперь вы можете сделать новый коммит (или несколько) на основе этих изменений.
4.2 Сохранить изменения в рабочей области, но не в индексе.
Эта команда просто перемещает указатель ветки, но не отражает изменения в индексе (он будет пустым).
4.3 Просто выбросить изменения.
Осторожно! Эта команда безвозвратно удаляет несохраненные текущие изменения. Если удаляемые коммиты не принадлежат никакой другой ветке, то они тоже будут потеряны.
Восстановление коммитов: Используйте git reflog и этот вопрос чтобы найти и восстановить коммиты; иначе сборщик мусора удалит их безвозвратно через некоторое время.
Восстановление несохраненных изменений: неиндексированные потеряны полностью, но вы можете восстановить то, что было проиндексировано.
5. Отменить уже опубликованные коммиты с помощью новых коммитов
Воспользуйтесь командой git revert . Она создает новые коммиты, по одному на каждый отменяемый коммит. Таким образом, если нужно отменить все коммиты после aaaaaa :
Восстановление: Если revert-коммит оказался ошибочным, используйте этот ответ.
Как откатить предпоследний коммит
- 2.39.1 → 2.39.2 no changes
- 2.39.0 12/12/22
- 2.38.1 → 2.38.4 no changes
- 2.38.0 10/02/22
- 2.37.1 → 2.37.6 no changes
- 2.37.0 06/27/22
- 2.30.1 → 2.36.5 no changes
- 2.30.0 12/27/20
- 2.27.1 → 2.29.3 no changes
- 2.27.0 06/01/20
- 2.23.1 → 2.26.3 no changes
- 2.23.0 08/16/19
Check your version of git by running
git-revert — Revert some existing commits
SYNOPSIS
DESCRIPTION
Given one or more existing commits, revert the changes that the related patches introduce, and record some new commits that record them. This requires your working tree to be clean (no modifications from the HEAD commit).
Note: git revert is used to record some new commits to reverse the effect of some earlier commits (often only a faulty one). If you want to throw away all uncommitted changes in your working directory, you should see git-reset[1], particularly the —hard option. If you want to extract specific files as they were in another commit, you should see git-restore[1], specifically the —source option. Take care with these alternatives as both will discard uncommitted changes in your working directory.
See «Reset, restore and revert» in git[1] for the differences between the three commands.
OPTIONS
Commits to revert. For a more complete list of ways to spell commit names, see gitrevisions[7]. Sets of commits can also be given but no traversal is done by default, see git-rev-list[1] and its —no-walk option.
With this option, git revert will let you edit the commit message prior to committing the revert. This is the default if you run the command from a terminal.
-m parent-number —mainline parent-number
Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows revert to reverse the change relative to the specified parent.
Reverting a merge commit declares that you will never want the tree changes brought in by the merge. As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want.
With this option, git revert will not start the commit message editor.
This option determines how the commit message will be cleaned up before being passed on to the commit machinery. See git-commit[1] for more details. In particular, if the <mode> is given a value of scissors , scissors will be appended to MERGE_MSG before being passed on in the case of a conflict.
Usually the command automatically creates some commits with commit log messages stating which commits were reverted. This flag applies the changes necessary to revert the named commits to your working tree and the index, but does not make the commits. In addition, when this option is used, your index does not have to match the HEAD commit. The revert is done against the beginning state of your index.
This is useful when reverting more than one commits’ effect to your index in a row.
GPG-sign commits. The keyid argument is optional and defaults to the committer identity; if specified, it must be stuck to the option without a space. —no-gpg-sign is useful to countermand both commit.gpgSign configuration variable, and earlier —gpg-sign .
Add a Signed-off-by trailer at the end of the commit message. See the signoff option in git-commit[1] for more information.
Use the given merge strategy. Should only be used once. See the MERGE STRATEGIES section in git-merge[1] for details.
Pass the merge strategy-specific option through to the merge strategy. See git-merge[1] for details.
After the rerere mechanism reuses a recorded resolution on the current conflict to update the files in the working tree, allow it to also update the index with the result of resolution. —no-rerere-autoupdate is a good way to double-check what rerere did and catch potential mismerges, before committing the result to the index with a separate git add .
Instead of starting the body of the log message with «This reverts <full object name of the commit being reverted>.», refer to the commit using «—pretty=reference» format (cf. git-log[1]). The revert.reference configuration variable can be used to enable this option by default.
SEQUENCER SUBCOMMANDS
Continue the operation in progress using the information in .git/sequencer . Can be used to continue after resolving conflicts in a failed cherry-pick or revert.
Skip the current commit and continue with the rest of the sequence.
Forget about the current operation in progress. Can be used to clear the sequencer state after a failed cherry-pick or revert.
Cancel the operation and return to the pre-sequence state.
EXAMPLES
Revert the changes specified by the fourth last commit in HEAD and create a new commit with the reverted changes.
git revert -n master
Revert the changes done by commits from the fifth last commit in master (included) to the third last commit in master (included), but do not create any commit with the reverted changes. The revert only modifies the working tree and the index.
CONFIGURATION
Everything below this line in this section is selectively included from the git-config[1] documentation. The content is the same as what’s found there:
Setting this variable to true makes git revert behave as if the —reference option is given.
Обзор команд Git для отмены изменений
Предположим, что мы случайно удалили файл, например myfile.txt :
Для его восстановления выполняем команду:
Отменяем индексирование файла
Допустим, мы по ошибке проиндексировали файл, выполнив команду git add myfilename . Для отмены этого действия воспользуемся командой:
Восстанавливаем предыдущие версии
Посмотрим, как восстановить более ранние версии в случае необходимости. С помощью команды git log переходим в историю коммитов, выбираем код SHA ранней версии (достаточно первых символов) и выполняем команду git checkout <SHA> :
Получаем следующее сообщение:
Вы увидите, что находитесь в более ранней версии. Вышеприведенная команда вывода checkout объясняет ситуацию. Эти изменения можно сохранить, если создать новую ветку.
Оказавшись в состоянии “detached HEAD” вполне можно запаниковать, не зная, как вернуться к последней версии в главной ветке master . Посмотрим, как это сделать:
Откатываем изменения на один коммит
Для отмены предыдущего коммита выполняем команду:
Удаляем неотслеживаемый файл
Допустим, вы добавили файлы, которые еще не подготовлены к коммиту. Чтобы от них избавиться, выполняем команду:
Это пробный запуск, который отображает файлы, подлежащие удалению. Подтверждаем выполнение этой операции командой:
Отменяем git init
Работая с Git, вы инициализируете проект с помощью git init . Для отмены данной операции просто удаляем файл .git из каталога.
Дополнительные команды: удаляем файл из удаленного репозитория
При работе с Git и GitHub/GitLab можно случайно отправить файл в удаленный репозиторий. В таком случае возникает необходимость его удалить. Рассмотрим ситуацию на примерах. Создаем удаленный репозиторий GitHub и локально его клонируем:
Меняем рабочий каталог на клонированный репозиторий. Как видно, здесь есть файлы README.md и .git .
Далее создаем файл wrong.txt , который мы отправим в удаленный репозиторий:
Файл wrong.txt добавлен в удаленный репозиторий.
Удаляем файл
При необходимости удалить файл как из удаленного каталога, так и локальной файловой системы выполняем команды:
Как видим, цель достигнута:
Удаляем файл из удаленного репозитория Git, но сохраняем его локально
Повторяем последовательность действий по созданию файла wrong.txt , который на этот раз мы удалим только из удаленного репозитория:
Отправляем файл wrong.txt :
Удаляем wrong.txt только из удаленного репозитория. Для этого используем тег cached .
Проверяем локальный репозиторий:
Проверяем удаленный каталог:
wrong.txt успешно удален из удаленного репозитория!
Со списком наиболее распространенных команд Git и GitHub вы можете ознакомиться по ссылке Git and GitHub Cheatsheet.
2.4 Основы Git — Операции отмены
В любой момент вам может потребоваться что-либо отменить. Здесь мы рассмотрим несколько основных способов отмены сделанных изменений. Будьте осторожны, не все операции отмены в свою очередь можно отменить! Это одна из редких областей Git, где неверными действиями можно необратимо удалить результаты своей работы.
Отмена может потребоваться, если вы сделали коммит слишком рано, например, забыв добавить какие-то файлы или комментарий к коммиту. Если вы хотите переделать коммит — внесите необходимые изменения, добавьте их в индекс и сделайте коммит ещё раз, указав параметр —amend :
Эта команда использует область подготовки (индекс) для внесения правок в коммит. Если вы ничего не меняли с момента последнего коммита (например, команда запущена сразу после предыдущего коммита), то снимок состояния останется в точности таким же, а всё что вы сможете изменить — это ваше сообщение к коммиту.
Запустится тот же редактор, только он уже будет содержать сообщение предыдущего коммита. Вы можете редактировать сообщение как обычно, однако, оно заменит сообщение предыдущего коммита.
Например, если вы сделали коммит и поняли, что забыли проиндексировать изменения в файле, который хотели добавить в коммит, то можно сделать следующее:
В итоге получится единый коммит — второй коммит заменит результаты первого.
Очень важно понимать, что когда вы вносите правки в последний коммит, вы не столько исправляете его, сколько заменяете новым, который полностью его перезаписывает. В результате всё выглядит так, будто первоначальный коммит никогда не существовал, а так же он больше не появится в истории вашего репозитория.
Очевидно, смысл изменения коммитов в добавлении незначительных правок в последние коммиты и, при этом, в избежании засорения истории сообщениями вида «Ой, забыл добавить файл» или «Исправление грамматической ошибки».
Отмена индексации файла
Следующие два раздела демонстрируют как работать с индексом и изменениями в рабочем каталоге. Радует, что команда, которой вы определяете состояние этих областей, также подсказывает вам как отменять изменения в них. Например, вы изменили два файла и хотите добавить их в разные коммиты, но случайно выполнили команду git add * и добавили в индекс оба. Как исключить из индекса один из них? Команда git status напомнит вам:
Прямо под текстом «Changes to be committed» говорится: используйте git reset HEAD <file>… для исключения из индекса. Давайте последуем этому совету и отменим индексирование файла CONTRIBUTING.md :
Команда выглядит несколько странно, но — работает! Файл CONTRIBUTING.md изменен, но больше не добавлен в индекс.
Команда git reset может быть опасной если вызвать её с параметром —hard . В приведенном примере файл не был затронут, следовательно команда относительно безопасна.
На текущий момент этот магический вызов — всё, что вам нужно знать о команде git reset . Мы рассмотрим в деталях что именно делает reset и как с её помощью делать действительно интересные вещи в разделе Раскрытие тайн reset главы 7.
Отмена изменений в файле
Что делать, если вы поняли, что не хотите сохранять свои изменения файла CONTRIBUTING.md ? Как можно просто отменить изменения в нём — вернуть к тому состоянию, которое было в последнем коммите (или к начальному после клонирования, или еще как-то полученному)? Нам повезло, что git status подсказывает и это тоже.
В выводе команды из последнего примера список изменений выглядит примерно так:
Здесь явно сказано как отменить существующие изменения. Давайте так и сделаем:
Как видите, откат изменений выполнен.
Важно понимать, что git checkout — <file> — опасная команда. Все локальные изменения в файле пропадут — Git просто заменит его версией из последнего коммита. Ни в коем случае не используйте эту команду, если вы не уверены, что изменения в файле вам не нужны.
Если вы хотите сохранить изменения в файле, но прямо сейчас их нужно отменить, то есть способы получше, такие как ветвление и припрятывание — мы рассмотрим их в главе Ветвление в Git.
Помните, все что попало в коммит почти всегда Git может восстановить. Можно восстановить даже коммиты из веток, которые были удалены, или коммиты, перезаписанные параметром —amend (см. Восстановление данных). Но всё, что не было включено в коммит и потеряно — скорее всего, потеряно навсегда.
Отмена действий с помощью git restore
Git версии 2.23.0 представил новую команду: git restore . По сути, это альтернатива git reset, которую мы только что рассмотрели. Начиная с версии 2.23.0, Git будет использовать git restore вместо git reset для многих операций отмены.
Давайте проследим наши шаги и отменим действия с помощью git restore вместо git reset .
Отмена индексации файла с помощью git restore
В следующих двух разделах показано, как работать с индексом и изменениями рабочей копии с помощью git restore . Приятно то, что команда, которую вы используете для определения состояния этих двух областей, также напоминает вам, как отменить изменения в них. Например, предположим, что вы изменили два файла и хотите зафиксировать их как два отдельных изменения, но случайно набираете git add * и индексируете их оба. Как вы можете убрать из индекса один из двух? Команда git status напоминает вам:
Прямо под текстом «Changes to be committed», написано использовать git restore —staged <file> … для отмены индексации файла. Итак, давайте воспользуемся этим советом, чтобы убрать из индекса файл CONTRIBUTING.md :
Файл CONTRIBUTING.md изменен, но снова не индексирован.
Откат измененного файла с помощью git restore
Что, если вы поймете, что не хотите сохранять изменения в файле CONTRIBUTING.md ? Как легко его откатить — вернуть обратно к тому, как он выглядел при последнем коммите (или изначально клонирован, или каким-либо образом помещён в рабочий каталог)? К счастью, git status тоже говорит, как это сделать. В выводе последнего примера, неиндексированная область выглядит следующим образом:
Он довольно недвусмысленно говорит, как отменить сделанные вами изменения. Давайте сделаем то, что написано:
Важно понимать, что git restore <file> — опасная команда. Любые локальные изменения, внесенные в этот файл, исчезнут — Git просто заменит файл последней зафиксированной версией. Никогда не используйте эту команду, если точно не знаете, нужны ли вам эти несохраненные локальные изменения.