diff --git a/docs/git/undo.md b/docs/git/undo.md new file mode 100644 index 0000000..44edace --- /dev/null +++ b/docs/git/undo.md @@ -0,0 +1,134 @@ +# Отмена действий в Git + +## Отмена изменений до `git add` + +Вернуть состояние файла к последнему коммиту. + +```sh +git restore +``` + + +## Отмена изменений после `git add` до `git commit` + +Если нежелательные изменения уже проиндексированы, т. е. выполнена команда `git add`. + +```sh +# Отменяет git add +git restore --staged +# Возвращает к исходному состоянию +git restore +``` + +Можно и одной командой. + +```sh +git reset --hard +``` + + +## Отмена коммитов до `git push` + +### Изменение последнего коммита + +Часто нужно что-то добавить/исправить/удалить в последнем коммите, в том числе сообщение коммита. + +```sh +# Добавляем/исправляем/удаляем +# Индексируем изменения (git add) +git commit -m "New message" --amend +``` + +Можно без `-m` и сообщения, если не хотим его менять, тогда надо будет просто закрыть открывшийся редактор, либо добавить флаг `--no-edit`, чтобы `Git` даже и не предлагал изменять сообщение. + +```sh +# Добавляем/исправляем/удаляем +# Индексируем изменения (git add) +git commit --amend --no-edit +``` + +!!! warning "Коммит не должен быть запушен!" + + Команда `git commit --amend` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`). + +### Удаление последних коммитов + +Можно полностью удалить коммит из истории репозитория с помощью `git reset`. + +```sh +# Можно добавить --hard, чтобы откатить изменения и вернуть рабочий каталог +# к состоянию, в котором он был на предыдущем коммите +git reset HEAD~1 +``` + +После такого удаления можно делать `git push`, не опасаясь, что кто-нибудь ещё сможет получить доступ к данным коммита, его следы могут остаться лишь в локальной копии репозитория. + +!!! warning "Коммит не должен быть запушен!" + + Команда `git reset` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`). + +??? info "Подробнее про `git reset`" + + Команда `git reset ` просто перемещает текущую ветку и `HEAD` на указанный коммит. Соответственно, вместо `HEAD~1` (указатель на предыдущий коммит) можно указать любой другой коммит в текущей ветке, например, `HEAD~3` - позволит отменить последние 3 коммита. Разумеется можно указать хэш коммита, к которому нужно откатиться. + + Параметр `--hard` говорит о том, что нужно сбросить и индекс и рабочий каталог до состояния указанного коммита. Есть и другие варианты: `--soft` - оставить изменения в индексе и рабочем каталоге, `--mixed` (значение по умолчанию) - сбросить индекс, но не трогать рабочий каталог. + +??? info "Как восстановить коммиты, удалённые с помощью `git reset`" + + `Git` ведёт локальный журнал всех операций, так что хэш коммита ещё можно восстановить. + + ```sh + git reflog + ``` + + Зная хэш коммита, можно вернуть состояние ветки к нему с помощью того же `git reset`. + + ```sh + git reset + ``` + + Или, например, вынести его в отдельную ветку. + + ```sh + git checkout + git checkout -b branch-with-restored-commit + ``` + + +## Отмена коммитов после `git push` + +### Отмена изменений последнего коммита + +Если коммит уже оказался в удалённом репозитории, то самый простой вариант - просто сделать новый коммит, который будет отменять изменения предыдущего. + +```sh +git revert HEAD +``` + +Можно указать диапазон коммитов, которые нужно отменить, или перечислить их хэши. + +```sh +# Создаст коммит с отменой изменений последних трёх коммитов +git revert HEAD~3..HEAD +``` + +После `git revert` можно спокойно делать `git push`, не опасаясь, что возникнут какие-либо конфликты с удалённым репозиторием. + +!!! warning "Изменения сохраняются в репозитории" + + Никакие данные ни из локального, ни из удалённого репозитория не удаляются, все коммиты остаются в истории той же самой ветки. `git revert` не подходит, если нужно полностью удалить нежелательные изменения (например, конфиденциальные данные) из локального и удалённого репозитория. + +### Удаление последних коммитов из удалённого репозитория + +Переписывать историю удалённого репозитория можно если над репозиторием работает один человек, либо, если на сервер была отправлена конфиденциальная информация, которую непременно нужно удалить, в других случаях лучше использовать `git revert`. + +Для отмены коммитов можно использовать методы, описанные в предыдущем разделе, а затем перезаписать историю удалённого репозитория. + +```sh +git push --force +``` + +## Полезные ссылки + - GitHowTo [12](https://githowto.com/ru/undoing_local_changes), [13](https://githowto.com/ru/undoing_staged_changes), [14](https://githowto.com/ru/undoing_committed_changes), [15](https://githowto.com/ru/removing_commits_from_a_branch). + - [Отмена нескольких коммитов](https://stackoverflow.com/questions/1463340/how-can-i-revert-multiple-git-commits#answer-1470452). + - [Удаление запушенного коммита](https://stackoverflow.com/questions/3293531/how-to-permanently-remove-few-commits-from-remote-branch#answer-41726152). \ No newline at end of file