Git reset - что делает команда, работа с режимами soft (для head), mixed и hard - Академия Selectel

Что делает команда Git reset

Инструкция про то, как работать с командой git reset: как управлять историей коммитов, контролировать состояние репозитория и не только.

Введение

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

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

Что такое git reset

Git reset является командой в Git, которая сбрасывает состояние указателя HEAD до определенного. Рассмотрим работу данной команды подробнее.

git reset [--<режим>] [<коммит>]

3 состояния файлов в Git

Функциональность git reset связана с управлением состоянием в трех областях: рабочая директория, индекс и репозиторий. В процессе работы системы контроля версий происходит взаимодействие с этими областями.

Пример

Мы редактируем файлы в своей рабочей директории (в рабочем каталоге) — это и есть наша локальная папка с файлами, над которыми мы работаем. После этого вводим git add, чтобы передать изменения из рабочей директории в раздел проиндексированных файлов. Этот раздел также называется индексом — промежуточной областью, необходимой, чтобы не производить никаких манипуляций в рабочем каталоге. Далее используется команда git commit: файлы из индекса попадают в репозиторий, в котором зафиксируются новые изменения.

Файлы и репозиторий

Какой будет результат выполнения git reset определяется параметрами —soft, —mixed, —hard.  Мы их рассмотрим чуть позднее.

Объяснение работы команды git reset

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

  • Определение типа сброса. Мы определяем ожидаемый результат от команды git reset определенными параметрами. В соответствии с целями ее использования, можно вернуть предыдущее состояние репозитория (отмена фиксации изменений), индекса (готовые к сохранению изменения), либо рабочей директории (изменения, которые еще не отслеживаются Git). Как говорилось ранее, это определяется параметрами —soft, —mixed, —hard.
  • Перенос HEAD. После применения git reset указатель HEAD сместится на нужный коммит.
  • Обновление индекса и рабочей директории. После выбранного типа сброса обновляется состояние индекса и рабочей директории в соответствии с указанным коммитом (по умолчанию указывается последний коммит).
  • Сброс коммита. Git reset возвращает репозиторий к одному из предыдущих состояний, что позволяет исправить ошибки или добавить что-то

Git reset —soft. Режим отмены коммита

Команда git reset —soft позволяет отменить последние коммиты. Сами изменения, которые были внесены в файлы, остаются в индексе для их дальнейшей модификации и последующего коммита.

git reset --soft [<коммит>]

Когда используется опция —soft, указатель HEAD перемещается на выбранное количество коммитов назад, а изменения остаются в разделе проиндексированных файлов и рабочем каталоге. Дерево объектов также не изменяется.

Пример

Предположим, мы создали новый файл и включили его в список отслеживаемых изменений. После этого мы произвели коммит, однако допустили ошибку в его описании. В таком случае задействуем параметр —soft для git reset, чтобы сбросить последний коммит и поместить файл обратно в индекс. 

Создаем новый файл example.py и добавляем его в индекс:

git add example.py

Создаем ошибочный коммит.

git commit -m "wrong commit"

Возвращаем предыдущее состояние репозитория параметром soft.

git reset --soft HEAD~1

Проверяем статус изменений, чтобы быть уверенными, что команды были введены верно.

git status

Мы увидим, что изменения в example.py были возвращены в индекс:

On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   example.py

Теперь просто создаем новый коммит с корректным описанием.

git commit -m “not wrong commit”

Таким же образом можно убрать два последних коммита, создав из них один общий, если такая потребность возникнет. Например:

git reset --soft HEAD~1
git commit

Получается аналог git commit —amend, но без лишней истории.

Режим soft

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

Git reset —mixed. Режим по умолчанию

Командой git reset —mixed пользуются для отмены последнего сохранения изменений в коммите и сброса индекса. Состояние рабочей директории остается нетронутым. Это позволяет добавить другие изменения в индекс для последующего коммита. Но дерево объектов при этом изменяется. Параметр –mixed действует по умолчанию.

git reset –mixed [<коммит>]

Пример

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

Например, сейчас в репозитории main_file.txt, updates.txt и prototype.txt.

git add main_file.txt
git add updates.txt
git add prototype.txt
git commit

Последний файл, prototype.txt, является лишним изменением. Его нужно убрать из репозитория, не удаляя. Чтобы добиться этого, задействуем  git reset —mixed (либо без объявления параметра, так как mixed стоит по умолчанию).

git reset HEAD~1

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

Добавляем только нужные нам main_file.txt и updates.txt.

git add main_file.txt
git add updates.txt

И финальный коммит.

git commit -m "Added files 1 and 3, excluding file 2"
Режим mixed

В итоге с помощью режима —mixed не был включен ненужный файл в коммит.

Git reset —hard. Режим удаления файлов

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

Пример

Предположим, у нас есть две ветки: feature и develop. Ветка develop была нежелательно обновлена из-за случайного коммита. Такие изменения требуется удалить. В этой ситуации подойдет использование параметра —hard.

git add mistakes.txt
git commit -m "bad commit"

Мы обнаружили добавленный файл, который не предназначался для включения в ветку develop, и нам нужно удалить его из истории Git полностью. Применяем git reset —hard, тем самым удаляя последний коммит и все связанные с ним изменения:

git reset --hard HEAD~1

Файл mistakes.txt в рабочей директории был удален вместе с последним коммитом. 

После мы можем перейти на нужную нам feature ветку, чтобы продолжить на ней разрабатывать.

git checkout feature
Режим hard

Резюмируя, при работе с git reset —hard, необходимо быть внимательными, так как ее неосторожное использование может привести к потере файлов. Если же изменения нужно просто перенести из одной ветки в другую без их удаления, то лучше использовать git cherry-pick.

Использование команды для совместной работы в одной ветке

Команда git reset используется для изменения состояния локального репозитория и не имеет никакого отношения к удаленному репозиторию. Однако данная команда дает возможность переписать историю с помощью git push —force.

Пример

Предположим, что были случайно закоммичены изменения в локальном репозитории и отправлены в удаленный посредством ввода команды git push:

git add .

git commit -m "Oops, wrong changes"

git push origin master

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

git reset --hard HEAD~1

Чтобы изменения были удалены из удаленного репозитория, нужно использовать команду git push с параметром —force:

git push --force origin master

Тем самым удаленная ветка master была перезаписана на локальную версию ветки master, что помогло избавиться от последнего коммита.

Обратите внимание, что использование опции —force может быть опасно, потому что она позволяет перезаписать историю в удаленном репозитории, что может нарушить работу других разработчиков. Поэтому необходимо использовать эту команду с осторожностью и предупреждать других разработчиков, если будут изменения истории в общем репозитории.

Использование git reset — полезный инструмент при работе с Git. Но необходимо понимать, как его использование влияет на историю репозитория и как правильно использовать данную команду для предотвращения потери данных и конфликтов в работе других разработчиков.

Сравнение с git revert

Команды git reset и git revert используются для отмены изменений в Git, но работают они по-разному.

Команда git reset меняет историю репозитория, сдвигая указатель ветки на конкретный коммит. Git revert создает новый коммит. Он откатывает изменения, внесенные в выбранный коммит, и оставляет историю репозитория неизменной.

  • Если потребность в том, чтобы избавиться от нескольких последних коммитов в истории репозитория, то можно использовать git reset —hard.
  • Если необходимо избавиться от конкретного коммита, использовать git revert с указанием хеша коммита — более подходящий вариант.
 git reset и git revert

Заключение

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

Несмотря на все ее возможности необходимо быть осторожными при использовании команды git reset, так как она изменяет историю репозитория, результатом чего может стать потеря данных. Поэтому мы рекомендуем создавать резервные копии репозитория, либо предварительно знать способы восстановления изменений, например, с использованием команды git reflog.