При использовании git иногда возникает необходимость откатывать изменения. Причиной тому могут быть внезапно возникшие баги, которые не удалось выявить на этапе тестирования. А если речь идет о локальном репозитории, то причин может быть еще больше.
Создадим тестовый репозиторий с которым будет проводить эксперименты:
bash> git init Инициализирован пустой репозиторий Git в /home/byurrer/reps/revert/.git/ > touch file.txt > git commit -am 'added file.txt' [master (корневой коммит) b9d0814] added file.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file.txt > echo "1" >> file.txt > git commit -am 'writed 1' [master 7c94686] writed 1 1 file changed, 1 insertion(+) > echo "2" >> file.txt > git commit -am 'writed 2' [master 911d49a] writed 2 1 file changed, 1 insertion(+) > echo "3" >> file.txt > git commit -am 'writed 3' [master 894f7ff] writed 3 1 file changed, 1 insertion(+) > git log --oneline 894f7ff (HEAD -> master) writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
reset
- инструмент изменения истории по 3 направлениям, иначе их называют деревья (дополнительно про деревья можн опочитать здесь и здесь):
git reset
не должна применяться к публичной истории, так как при синхронизации между участниками разработки, их локальные репозитории будут отставать.Команда git reset
имеет 3 опции, каждая из которых возвдейтвует на определенный набор деревьев:
--soft
- меняет дерево коммитов--mixed
- меняет дерево коммитов и раздел проиндексированных файлов--hard
- меняет дерево коммитов, раздел проиндексированных файлов и рабочий каталогfile.txt
еще одну строку, закоммитим и просмотрим состояние репозитория:
bash> echo "4" >> file.txt > git commit -am 'writed 4' [master 5fafd6c] writed 4 1 file changed, 2 insertions(+) > git status На ветке master нечего коммитить, нет изменений в рабочем каталоге > git log --oneline 5fafd6c (HEAD -> master) writed 4 894f7ff writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
Сделаем мягкий сброс к предыдущему коммиту и посмотрим состояние репозитория:
bash> git reset --soft 894f7ff > git status На ветке master Изменения, которые будут включены в коммит: (use "git restore --staged <file>..." to unstage) изменено: file.txt > git log --oneline 894f7ff (HEAD -> master) writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
git reset --soft
сбрасывает только историю коммитов, не затрагивая рабочую директорию и раздел проиндексированных файлов. При помощи мягкого сброса можно удалять коммиты, без потери индекса данных и изменений файлов.Сделаем смешанный сброс к предыдущему коммиту и посмотрим состояние репозитория:
bash> git reset --mixed 894f7ff Непроиндексированные изменения после сброса: M file.txt > git status На ветке master Изменения, которые не в индексе для коммита: (используйте «git add <файл>…», чтобы добавить файл в индекс) (use "git restore <file>..." to discard changes in working directory) изменено: file.txt нет изменений добавленных для коммита (используйте «git add» и/или «git commit -a»)
git reset --mixed
сбрасывает историю коммитов и раздел проидексированных файлов, не затрагивая рабочую директорию. При помощи смешанного сброса можно удалять коммиты и индексированные данные, но без потери изменений файлов.Сделаем жесткий сброс к предыдущему коммиту и посмотрим состояние репозитория:
bash> git reset --hard 894f7ff HEAD сейчас на 894f7ff writed 3 > git status На ветке master нечего коммитить, нет изменений в рабочем каталоге > git log --oneline 894f7ff (HEAD -> master) writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
git reset --hard
сбрасывает все. Без возможности восстановления.revert
- безопасное исключение изменений коммита с сохранением истории и добавлением нового коммита.Пробуем отменить последний коммит:
bash> git revert HEAD Revert "writed 3" This reverts commit d3e6055f6a8a55fb9f4c84c8c061edc7dbab2d93. # Пожалуйста, введите сообщение коммита для ваших изменений. Строки, # начинающиеся с «#» будут проигнорированы, а пустое сообщение # отменяет процесс коммита. # # На ветке master # Ваша ветка обновлена в соответствии с «origin/master». # # Изменения, которые будут включены в коммит: # изменено: file.txt #
Посмотрим историю репозитория:
bash> git log --oneline 136b515 (HEAD -> master) Revert "writed 3" 894f7ff writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
Вернем все как было (для чистоты следующих примеров):
bash> git reset --hard 894f7ff HEAD сейчас на 894f7ff writed 3
Попробуем отменить коммит ниже текущего:
bash> git revert 911d49a Автослияние file.txt КОНФЛИКТ (содержимое): Конфликт слияния в file.txt error: не удалось обратить изменения коммита 911d49a… writed 2 подсказка: после разрешения конфликтов, пометьте исправленные пути подсказка: с помощью «git add <пути>» или «git rm <пути>» подсказка: и сделайте коммит с помощью «git commit»
Еще раз взглянем на историю репозитория:
bash> git log --oneline 894f7ff (HEAD -> master) writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
911d49a
остается коммит 894f7ff
после него, который вносит изменения в тот же файл что и коммит 911d49a
, что ведет к конфликту слияния.В данном случае есть вариант решать конфликт вручную, либо произвести серию отмен (revert
) коммитов до нужного коммита:
bash> git revert 7c94686..HEAD [master 8f2ff6a] Revert "writed 3" 1 file changed, 1 deletion(-) [master 97aaf82] Revert "writed 2" 1 file changed, 1 deletion(-)
Посмотрим историю:
bash> git log --oneline 97aaf82 (HEAD -> master) Revert "writed 2" 8f2ff6a Revert "writed 3" 894f7ff writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
7c94686
, HEAD
], то есть отменяется все начиная от HEAD
и до 7c94686
, но не включая 7c94686
.Теперь добавим в репозиторий новый файл:
bash> touch file2.txt > git add . > git commit -m 'added file2.txt' [master ae1a44b] added file2.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file2.txt > git log --oneline ae1a44b (HEAD -> master) added file2.txt 894f7ff writed 3 911d49a writed 2 7c94686 writed 1 b9d0814 added file.txt
Появился новый коммит не затрагивающий изменения файла file.txt
. Попробуем отменить коммит ниже текущего и отменить последние изменения в файле file.txt
:
bash> git revert 894f7ff [master 136b515] Revert "writed 3" 1 file changed, 1 deletion(-)
Очевидно, конфликтов нет.
revert
отменяет изменения коммита не удаляя его из истории, создавая при этом новый коммитgit revert
предназначена для безопасной отмены публичных коммитов, а git reset
- для отмены локальных изменений.