1. Введение
При использовании Git нам часто приходится отменять или возвращать фиксацию, будь то откат к определенному моменту времени или откат особенно проблемной фиксации. В этом руководстве мы рассмотрим наиболее распространенные команды для отмены и возврата коммитов в Git. Мы также продемонстрируем тонкие различия в том, как работают эти команды.
2. Просмотр старых коммитов с помощью git checkout
Начнем с того, что мы можем просмотреть состояние проекта при определенной фиксации с помощью команды git checkout
. Мы можем просмотреть историю репозитория Git с помощью команды git log
. Каждый коммит имеет уникальный идентификационный хэш SHA-1, который мы можем использовать с git checkout
для повторного посещения любого коммита на временной шкале.
В этом примере мы вернемся к коммиту, который имеет идентификационный хэш e0390cd8d75dc0f1115ca9f350ac1a27fddba67d
: ``
git checkout e0390cd8d75dc0f1115ca9f350ac1a27fddba67d
Наш рабочий каталог
теперь будет точно соответствовать состоянию указанного коммита. Таким образом, мы можем просматривать проект в его историческом состоянии и редактировать файлы, не беспокоясь о потере текущего состояния проекта. Ничего из того, что мы здесь делаем, не сохраняется в хранилище. Это известно как отсоединенное
состояние HEAD.
Мы можем использовать git checkout
для локально измененных файлов, чтобы восстановить их версии рабочей копии
.
3. Откат фиксации с помощью git revert
Мы отменяем коммит в Git с помощью команды git revert
. Важно помнить, что эта команда не является традиционной операцией отмены. Вместо этого он инвертирует изменения, внесенные фиксацией, и создает новую фиксацию с обратным содержимым.
Это означает, что git revert
следует использовать только в том случае, если мы хотим применить инверсию конкретного коммита . Он не возвращается к предыдущему состоянию проекта путем удаления всех последующих коммитов — он отменяет один коммит.
git revert
не перемещает указатели ref на фиксацию, которую мы отменяем, в отличие от других команд «отмены», таких как git checkout
и git reset
. Вместо этого эти команды перемещают указатель HEAD ref на указанный коммит.
Давайте рассмотрим пример отмены коммита:
mkdir git_revert_example
cd git_revert_example/
git init .
touch test_file
echo "Test content" >> test_file
git add test_file
git commit -m "Adding content to test file"
echo "More test content" >> test_file
git add test_file
git commit -m "Adding more test content"
git log
git revert e0390cd8d75dc0f1115ca9f350ac1a27fddba67d
cat test_file
В этом примере мы создали файл test_file, добавили некоторый контент и зафиксировали его. Затем мы добавили и зафиксировали дополнительный контент в файле перед запуском журнала git,
чтобы определить хэш
фиксации, которую мы хотим отменить.
В данном случае мы откатываем самую последнюю фиксацию. Наконец, мы запустили git revert
и убедились, что изменения в коммите были отменены, выведя содержимое файла.
4. Возврат к предыдущему состоянию проекта с помощью git reset
Возврат к предыдущему состоянию в проекте с Git достигается с помощью команды git reset
. Этот инструмент отменяет более сложные изменения. Он имеет три основные формы вызова, относящиеся к внутренней системе управления состоянием
Git : –hard
, –soft
и –mixed
. Понимание того, какой вызов использовать, является наиболее сложной частью выполнения git revert
.
git reset
похож на git checkout
. Однако git reset
будет перемещать указатель HEAD
ref, тогда как git checkout
работает с указателем HEAD
ref и не перемещает его .
Чтобы понять различные вызовы, мы рассмотрим внутреннюю систему управления
состоянием Git, также известную как три дерева
Git .
Первое дерево — это рабочий каталог
. Это дерево синхронизировано с локальной файловой системой и представляет немедленные изменения, внесенные в содержимое файлов и каталогов.
Далее у нас есть промежуточное индексное дерево
. Это дерево отслеживает изменения в рабочем каталоге
— другими словами, изменения, которые были выбраны с помощью git add
для сохранения в следующем коммите.
Последнее дерево — это история коммитов
. Команда git commit
добавляет изменения в постоянный снимок, который хранится в истории коммитов
.
4.1. -жесткий
Наиболее опасная и часто используемая опция с этим вызовом — это указатели ссылок на историю коммитов для обновления до указанного коммита. После этого промежуточный индекс
и рабочий индекс
сбрасываются, чтобы соответствовать указанному коммиту. Все ранее отложенные изменения промежуточного индекса
и рабочего каталога
сбрасываются в соответствии с состоянием дерева коммитов
. Мы потеряем любую незавершенную или незафиксированную работу в промежуточном индексе
и рабочем индексе
.
Следуя приведенному выше примеру, давайте добавим еще немного контента в файл, а также зафиксируем новый файл в репозиторий:
echo "Text to be committed" >> test_file
git add test_file
touch new_test_file
git add new_test_file
git commit -m "More text added to test_file, added new_test_file"
Допустим, мы решили вернуться к первому коммиту в репозитории. Мы добьемся этого, выполнив команду:
git reset --hard 9d6bedfd771f73373348f8337cf60915372d7954
Git сообщит нам, что HEAD
теперь находится в указанном хэше коммита. Глядя на содержимое test_file
, мы видим, что наши последние текстовые добавления отсутствуют, а наш new_test_file
больше не существует . Эта потеря данных необратима, поэтому очень важно, чтобы мы понимали, как –hard
работает с тремя деревьями Git.
4.2. -мягкий
Когда вызов происходит с параметром –soft
, указатели ref обновляются, и сброс останавливается на этом. Таким образом, промежуточный индекс
и рабочий каталог
остаются в том же состоянии.
В нашем предыдущем примере изменения, которые мы зафиксировали в промежуточном индексе
, не были бы удалены, если бы мы использовали аргумент –soft
. Мы по-прежнему можем зафиксировать наши изменения в промежуточном индексе
.
4.3. смешанный
Режим работы по умолчанию, если аргумент не передан, –mixed
предлагает промежуточный вариант между вызовами –soft
и –hard
. Промежуточный индекс
сбрасывается до состояния указанного обновления указателей фиксации и ссылки. Любые отмененные изменения из промежуточного индекса
перемещаются в рабочий каталог
.
Использование –mixed
в нашем примере выше означает, что наши локальные изменения в файлах не удаляются. Однако, в отличие от –soft
, изменения в промежуточном индексе
отменяются и ожидают дальнейших действий .
5. Вывод
Простой способ сравнить два метода заключается в том, что git revert
безопасен, а git reset
опасен . Как мы видели в нашем примере, существует вероятность потери работы с помощью git reset
. С помощью git revert
мы можем безопасно отменить общедоступную фиксацию, тогда как git reset
предназначен для отмены локальных изменений в рабочем каталоге
и промежуточном индексе.
git reset
переместит указатель HEAD ref, тогда как
git revert
просто отменит фиксацию и применит отмену через новую фиксацию к HEAD
. Также важно отметить, что мы никогда не должны использовать git reset
, когда любые последующие снимки были отправлены в общий репозиторий . Мы должны предположить, что другие разработчики полагаются на опубликованные коммиты.