Сегодня трудно представить работу программиста или другого IT-специалиста без контроля версий. Среди инструментов SCM особое внимание заслуживает Git, который быстро завоевал всеобщую популярность и негласно считается стандартом в мире систем контроля версий. Git позволяет легко отслеживать изменения в файлах проекта, работать с функционалом веток, организовывать совместную разработку, хранить код и другие файлы централизованно. Также одной из сильных сторон Git является возможность гибкой отмены и удаления внесенных изменений. Одним из вариантов отмены изменений является команда git reset
, поддерживающая три режима работы. В рамках данной статьи мы рассмотрим, как можно отменить внесенные изменения при помощи команды git reset
, а также ее режимы работы на практических примерах.
Мы будем рассматривать практическую часть работы с git reset
, поэтому нам необходимо заранее установить Git.
В качестве теста будем использовать операционную систему на ядре Linux, а именно выберем дистрибутив Ubuntu версии 22.04. Но можно использовать абсолютно любой дистрибутив Linux на ваш вкус, так как git присутствует практически во всех современных пакетных менеджерах.
В большинстве дистрибутивов git уже предустановлен заранее, однако установленная версия может незначительно отличаться от последней выпущенной. Также для дистрибутивов Ubuntu git можно установить, используя официальный репозиторий. Для этого необходимо выполнить следующие команды:
add-apt-repository ppa:git-core/ppa && apt -y install git
Для всех остальных Debian-based-дистрибутивов (Debian, Linux Mint, Kali Linux и т.д.) установку можно выполнить из стандартных репозиториев ОС при помощи команды:
apt -y install git
Если же вы решите выбрать дистрибутивы, основанные на RHEL (RedHat, CentOS, Fedora, Oracle Linux), то команда установки будет варьироваться в зависимости от используемого пакетного менеджера:
# Пакетный менеджер yum:
yum -y install git
# Пакетный менеджер dnf:
dnf -y install git
После того как установка будет завершена, проверим корректность установки пакета при помощи вывода версии git:
git --version
Предназначение git reset
— отмена внесенных локальных изменений. Если говорить техническим языком, то команда git reset
сбрасывает указатель (ссылку) HEAD
на предыдущий коммит в репозитории. Под HEAD
понимается указатель на текущую ветку, который одновременно указывает еще и на последний коммит, сделанный в текущей ветке.
Команда git reset
оперирует тремя механизмами (сущностями): рабочим каталогом, указателем HEAD
и индексом. Все эти механизмы в терминологии git называются деревьями, т.к. их структура данных построена на основе узлов и указателей. Далее мы рассмотрим данные механизмы более подробно.
Отдельно стоит отметить, что в различных веб-сервисах, использующих git, включая GitHub, GitLab и Bitbucket, присутствует возможность отмены действий при помощи веб-интерфейса. Однако, как правило, вместо git reset
используется ее другой безопасный аналог — git revert
, которая отличает от reset тем, что не удаляет коммиты из истории проекта, тем самым сохраняя всю историю вносимых изменений.
Под рабочим каталогом понимается та директория, где была выполнена команда git reset
и хранятся файлы проекта, которые будут отправлены в репозиторий git. Для того чтобы система git понимала, какой каталог является рабочим, а какой нет, при выполнении команды git init
в корне директории создается скрытая директория с именем .git
, в которой хранятся конфигурационные файлы git.
Использование рабочего каталога выглядит следующим образом:
1) Создаем новый каталог и переходим в него:
mkdir new_project && cd new_project
2) Создаем новый репозиторий git:
git init
3) После того как был инициирован новый репозиторий, в корне директории будет создана скрытая папка с именем git с конфигурационными файлами git:
Все дальнейшие операции по добавлению, удалению или изменению файлов будут происходить в рабочем каталоге git.
HEAD
указывает на текущую ветку в репозитории, а ветка указывает на последний коммит в репозитории. Каждый раз при переключении на другую ветку (при помощи команды git checkout
) указатель HEAD
будет указывать на последний коммит в новой ветке.
На практике это выглядит следующим образом:
1) Создаем новый файл:
touch new1.txt
И добавляем его в репозиторий:
git add new1.txt
2) Делаем коммит:
git commit -m "Initial commit"
3) Самый простой способ увидеть указатель HEAD
— выполнить команду git cat-file
:
git cat-file -p HEAD
Так как у нас один коммит, то указатель HEAD
указывает на него.
4) Теперь внесем изменения в ранее созданный файл new1.txt
:
echo "This is test file" > new1.txt
Добавляем файл в git:
git add new1.txt
5) Делаем коммит:
git commit -m "Added some info to new1.txt file"
6) Повторно выполняем команду:
git cat-file -p HEAD
Как можно увидеть на скриншоте выше, указатель HEAD указывает уже на другой последний коммит в ветке.
Индекс в git — это промежуточная область, в которую добавляются файлы с использованием git add
. Индекс можно рассматривать как предварительный коммит — добавленные файлы уже присутствуют в репозитории и отслеживаются, но еще не добавлены в сам коммит. Стоит помнить, что когда файл находится в области индекса, он еще не является частью репозитория, и сам файл можно убрать из стадии индекса. Рассмотрим индекс на практике.
1) Создаем файл:
touch new2.txt
И добавляем его в индекс:
git add new2.txt
2) Проверяем статус:
git status
Файл находится в staging-этапе, он же индекс. Файл уже отслеживается git, но еще не добавлен в коммит.
vds
В своей работе git reset
может использовать три различных режима — Soft, Mixed, Hard.
Режим Soft используется для отмены последних внесенных изменений. С технической стороны при использовании режима soft
происходит возврат указателя HEAD
к указанному коммиту. При этом режим soft
сохраняет все изменения, внесенные в индекс.
На практике режим soft
работает следующим образом:
1) Создадим новый файл с именем new3.txt
в репозитории:
touch new3.txt
2) Добавляем его в индекс (staging):
git add new3.txt
3) Создаем коммит:
git commit -m “Added new3.txt file”
Смотрим историю коммитов:
git log
4) Теперь представим ситуацию, когда мы сделали коммит по ошибке и нам предстоит сделать откат:
git reset --soft HEAD~1
5) Проверяем историю коммитов:
git log
Как можно заметить на скриншоте выше, ранее присутствующий последний коммит, в котором был добавлен файл new3.txt
, был удален, и указатель HEAD
сместился на предыдущий коммит в репозитории.
Также режим soft
возвращает добавленные файлы обратно в индекс:
git status
Режим Mixed используется в команде git reset
по умолчанию. Режим mixed
производит отмену последнего внесенного изменения в коммите, а также сброс индекса. Сами файлы при этом не затрагиваются. На практике это выглядит следующим образом:
1) Создаем три файла и добавляем их по одному:
touch new{1..3}.txt
git add new1.txt
git add new2.txt
git add new3.txt
2) Далее создаем коммит:
git commit -m "Added three new files"
Текущая история коммитов выглядит следующим образом:
3) В итоге мы получили ситуацию, когда все три файла были добавлены в рамках одного коммита. Теперь нам необходимо отменить последнее внесенное изменение, в данном случае — отменить добавление файла с именем new3.txt
:
git reset HEAD~1
4) Проверяем историю коммитов после git reset --mixed
:
По итогу мы видим, что последний коммит был удален, а указатель HEAD
вернулся на коммит назад. Также проверяем статус области индекса:
git status
Файлы были возвращены в первоначальное состояние. Само содержимое файлов не затрагивается.
Третий и последний режим команды git reset
называется Hard, и основным его предназначением, помимо удаления коммитов, является удаление файлов в репозитории. Выглядит это следующим образом: режим hard
удаляет сам коммит, изменения в индексе и сами файлы. Данный режим колоссально отличается от двух других, так как по сути производит обновление рабочей директории до состояния индекса (до состояния, когда в репозитории были внесены какие-либо изменения).
1) Выведем список файлов в папке:
ls -l
Создаем новый файл:
touch readme.md
2) Добавляем файл readme.md
в индекс:
git add readme.md
Смотрим статус:
git status
3) Делаем новый коммит:
git commit -m "Added readme.md file"
4) Теперь предположим, что добавленный файл не должен присутствовать в репозитории и его необходимо удалить. Также необходимо удалить коммит и состояние индекса. Воспользуемся режимом hard
команды git reset
:
git reset --hard HEAD~1
Режим hard
выводит в терминале часть кэша того коммита, к которому был совершен откат.
5) Смотрим историю изменений:
git log
6) Проверяем статус индекса:
git status
Статус индекса тоже был удален.
7) Наконец проверяем файлы, которые остались в директории:
ls -l
Ранее созданный файл с именем readme.md
был успешно удален.
Режим
hard
необходимо использовать с повышенной осторожностью в связи с тем, что данный режим удаляет все внесенные изменения не только на уровне репозитория git (удаляет коммиты и статус индекса), но и на уровне файловой системы (удаляет файлы).
В начале статьи было упомянуто, что команда git reset
позволяет сделать откат не только на коммит назад (конструкция HEAD~1
), но и на любой другой, который присутствует в репозитории. Рассмотрим на конкретном практическом примере.
Предположим, что у нас присутствует репозиторий, в котором расположены четыре коммита:
git log
Последний коммит в репозитории — это коммит с хэш-суммой bae2f49660c0807e46579fa2708040f329bc4717
и сообщением Added extra files
. Например, следующая команда вернет состояние репозитория на коммит с хэш-суммой be401c3f5c55869ae04c333c9c19ef959634951a
и сообщением Added new2.txt file
:
git reset HEAD~1
Чтобы вернуться на два коммита назад, достаточно выполнить:
git reset HEAD~2
Проверяем текущий последний коммит:
git log
Также произвести откат можно при помощи хэш-суммы коммита. Достаточно выбрать один из трех режимов (например, hard
):
git reset --hard be401c3f5c55869ae04c333c9c19ef959634951a
Выгодные тарифы на VDS/VPS в Timeweb Cloud
В статье мы подробно ознакомились с git reset
и ее режимами: soft
, mixed
, hard
. Команда git reset
является мощным инструментом, обладающим богатым функционалом и позволяющим отменять внесенным изменения. Однако, для ее эффективного использования необходимо четко представлять, как работает каждый режим, и особенно аккуратно работать с режимом hard
, чтобы не допустить случайной и необратимой потери важных данных.