Отмена коммитов и изменений
В этом разделе мы обсудим доступные стратегии и команды Git для выполнения отмены изменений. Прежде всего необходимо отметить, что в Git не существует традиционной системы отмены, как в текстовых редакторах. Лучше воздержаться от сопоставления операций Git с какой бы то ни было традиционной концепцией отмены изменений. Кроме того, Git имеет собственную систему терминов для операций отмены, и в обсуждении лучше всего использовать их. В числе таких терминов — сброс (reset), возврат (revert), переключение (checkout), очистка (clean) и другие.
Git можно рассматривать как инструмент для управления временной шкалой. Коммиты — это снимки моментов времени или точек интереса на временной шкале истории проекта. Кроме того, с помощью веток можно управлять несколькими временными шкалами. Когда вы выполняете операцию отмены в Git, вы, как правило, перемещаетесь назад во времени или на другую временную шкалу, где ошибок не было.
Это руководство предоставляет все необходимые навыки для работы с предыдущими версиями проекта по разработке программного обеспечения. Сначала мы рассмотрим, как исследовать старые коммиты, а затем изучим разницу между отменой публичных коммитов в истории проекта и сбросом неопубликованных изменений на локальном компьютере.
Поиск утерянного: просмотр старых коммитов
В основе любой системы управления версиями лежит идея хранения «безопасных» копий проекта, чтобы у разработчиков не возникало опасений безвозвратно испортить базу кода. Когда в проекте сохранена история коммитов, можно повторно оценивать и анализировать любые ранее выполненные коммиты. Один из лучших инструментов для просмотра истории репозитория Git — команда git log
. В примере ниже мы используем git log для получения последних коммитов популярной графической библиотеки с открытым исходным кодом.
git log --oneline
e2f9a78fe Replaced FlyControls with OrbitControls
d35ce0178 Editor: Shortcuts panel Safari support.
9dbe8d0cf Editor: Sidebar.Controls to Sidebar.Settings.Shortcuts. Clean up.
05c5288fc Merge pull request #12612 from TyLindberg/editor-controls-panel
0d8b6e74b Merge pull request #12805 from harto/patch-1
23b20c22e Merge pull request #12801 from gam0022/improve-raymarching-example-v2
fe78029f1 Fix typo in documentation
7ce43c448 Merge pull request #12794 from WestLangley/dev-x
17452bb93 Merge pull request #12778 from OndrejSpanel/unitTestFixes
b5c1b5c70 Merge pull request #12799 from dhritzkiv/patch-21
1b48ff4d2 Updated builds.
88adbcdf6 WebVRManager: Clean up.
2720fbb08 Merge pull request #12803 from dmarcos/parentPoseObject
9ed629301 Check parent of poseObject instead of camera
219f3eb13 Update GLTFLoader.js
15f13bb3c Update GLTFLoader.js
6d9c22a3b Update uniforms only when onWindowResize
881b25b58 Update ProjectionMatrix on change aspect
Связанные материалы
Шпаргалка по Git
СМ. РЕШЕНИЕ
Изучите Git с помощью Bitbucket Cloud
Каждый коммит имеет уникальный идентифицирующий хеш SHA-1. Эти идентификаторы используются для перемещения по временной шкале коммитов и возвращения к коммитам. По умолчанию git log
показывает только коммиты текущей выбранной ветки. Но не исключено, что искомый коммит находится в другой ветке. Для просмотра всех коммитов во всех ветках используется команда git log --branches=*
. Команда git branch позволит просмотреть и посетить другие ветки. Так, с помощью сочетания git branch -a
вам будет возвращен список имен всех известных веток. Просмотреть журнал коммитов одной из этих веток можно посредством команды git log <имя_ветки>
.
После того как вы нашли ссылку на нужный коммит в истории, для перехода к нему можно использовать команду git checkout
. Команда git checkout
— это простой способ «загрузить» любой из этих сохраненных снимков на компьютер разработчика. При стандартном процессе разработки указатель HEAD
обычно указывает на главную ветку main
или другую локальную ветку. Но при переключении на предыдущий коммит HEAD
указывает уже не на ветку, а непосредственно на сам коммит. Такая ситуация называется состоянием открепленного указателя HEAD
, и ее можно представить так:
Переход к старой версии файла не перемещает указатель HEAD
. Он остается в той же ветке и в том же коммите, что позволяет избежать открепления указателя HEAD. После этого можно выполнить коммит старой версии файла в новый снимок состояния, как и в случае других изменений. Соответственно, такое использование команды git checkout
применительно к файлу позволяет откатиться к прежней версии отдельного файла. Для получения дополнительной информации об этих двух режимах посетите страницу команды git checkout.
Просмотр старых версий
В этом примере предполагается, что вы начали разработку безумного эксперимента, но не уверены, хотите его сохранить или нет. Чтобы принять решение, вы хотите взглянуть на состояние проекта до начала эксперимента. Прежде всего, нужно найти идентификатор редакции, которую вы хотите просмотреть.
git log --oneline
Допустим, история вашего проекта выглядит примерно так:
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
Для просмотра коммита «Make some important changes to hello.txt» можно использовать команду git checkout
в следующем виде:
git checkout a1e8fb5
Это приведет к тому, что ваш рабочий каталог будет в точности соответствовать состоянию коммита a1e8fb5
. Вы можете просматривать файлы, компилировать проект, запускать тесты и даже редактировать файлы, не боясь потерять текущее состояние проекта. Никакие внесенные здесь изменения не будут сохранены в репозитории. Чтобы продолжить разработку, необходимо вернуться к текущему состоянию проекта:
git checkout main
Предположим, вы ведете разработку в основной ветке main
по умолчанию. При каждом возвращении в ветку main
можно использовать команду git revert или git reset для отмены нежелательных изменений.
Отмена коммита снимка
Технически существует несколько стратегий отмены коммитов. В дальнейших примерах предполагается, что история коммитов выглядит следующим образом:
git log --oneline
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
Займемся отменой коммита 872fa7e Try something crazy
. Возможно, безумный эксперимент зашел слишком далеко.
Отмена коммита с помощью git checkout
С помощью команды git checkout
мы можем перейти к предыдущему коммиту , a1e8fb5,
и вернуть репозиторий в состояние, предшествовавшее этому безумному коммиту. Переход к отдельному коммиту переведет репозиторий в состояние открепленного указателя HEAD. Работа при этом перестает принадлежать какой-либо из веток. При открепленном указателе HEAD все новые коммиты будут оставаться без родителя, пока вы не вернете ветки в положенное состояние. «Сборщик мусора» в Git удаляет коммиты без родителя. Этот сервис работает с определенными интервалами и удаляет такие коммиты без возможности восстановления. Чтобы такие коммиты не были удалены «сборщиком мусора», перед их выполнением нужно убедиться, что мы работаем в ветке.
При наличии открепленного указателя HEAD можно выполнить команду git checkout -b new_branch_without_crazy_commit
. Она создаст новую ветку с именем new_branch_without_crazy_commit
и совершит переход в это состояние. Теперь репозиторий находится на новой временной шкале, где нет коммита 872fa7e
. На этом этапе мы можем продолжить работу в новой ветке, поскольку коммита 872fa7e
не существует и его можно считать «отмененным». К сожалению, если вам нужна предыдущая ветка (возможно, это основная ветка main
), такая стратегия не подходит. Поэтому рассмотрим другие варианты отмены. Более детальную информацию и примеры см. в нашей подробной статье о git checkout.
Отмена публичного коммита с помощью git revert
Предположим, мы вернулись к исходному примеру истории коммитов. Истории, в которую входит коммит 872fa7e
. В этот раз попробуем отмену путем обратной операции. При исполнении команды git revert HEAD
Git создаст новый коммит с операцией, обратной последнему коммиту. В текущую историю ветки будет добавлен новый коммит, и она будет выглядеть следующим образом:
git log --oneline
e2f9a78 Revert "Try something crazy"
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
На этом этапе мы снова технически «отменили» коммит 872fa7e
. Хотя коммит 872fa7e
по-прежнему существует в истории, новый коммит e2f9a78
отменил изменения 872fa7e
. В отличие от нашей предыдущей стратегии переключения с помощью команды checkout, мы можем продолжить работать с этой же веткой, поэтому данная стратегия является удовлетворительной. Это идеальный способ отмены при работе в открытых общих репозиториях, однако если у вас есть требование вести минимальную «очищенную» историю Git, эта стратегия может не подойти.
Отмена коммита с помощью git reset
Рассмотрение этой стратегии отмены мы продолжим на нашем рабочем примере. Команда git reset — это расширяемая команда с разнообразными функциями и вариантами использования. Если мы выполним команду git reset --hard a1e8fb5
, история коммитов будет сброшена до указанного коммита. Теперь при просмотре истории с помощью git log
отобразится следующее:
git log --oneline
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
Вывод команды log показывает, что коммиты e2f9a78
и 872fa7e
больше не существуют в истории. На этом этапе мы можем продолжить работу и создавать новые коммиты так, словно «безумных» коммитов никогда не было. Этот метод отмены изменений оставляет историю максимально чистой. Отмена с помощью команды reset отлично подходит для локальных изменений, но при работе в общем удаленном репозитории создает сложности. Если у нас есть общий удаленный репозиторий, в котором с помощью команды push опубликован коммит 872fa7e
, и мы попытаемся выполнить команду git push
для ветки, в которой с помощью команды reset была сброшена история, система Git обнаружит это и выдаст ошибку. Git будет считать, что публикуемая ветка не была обновлена, поскольку в ней отсутствуют коммиты. В таких случаях лучше использовать отмену с помощью команды git revert
.
Отмена последнего коммита
В предыдущем разделе мы рассмотрели различные стратегии отмены коммитов. Их также можно применять и к последнему коммиту. Однако иногда последний коммит можно не удалять и не сбрасывать. Например, бывают ситуации, когда коммит выполняется преждевременно. В этом случае его можно исправить. После того как вы внесете дополнительные изменения в рабочий каталог и добавите их в раздел проиндексированных файлов с помощью git add, выполните команду git commit --amend
. При этом система Git откроет настроенный системный редактор, где вы сможете изменить комментарий к последнему коммиту. Новые изменения будут добавлены в исправленный коммит.
Отмена неотправленных изменений
Пока не выполнен коммит изменений в историю репозитория, они находятся в разделе проиндексированных файлов и в рабочем каталоге. Вам может потребоваться отменить изменения в этих двух областях. Раздел проиндексированных файлов и рабочий каталог являются внутренними механизмами управления состоянием Git. Подробную информацию о том, как работают эти два механизма, см. на странице git reset, где приводится их описание.
Рабочий каталог
Рабочий каталог обычно синхронизируется с локальной файловой системой. Чтобы отменить изменения в рабочем каталоге, можно просто изменить файлы с помощью привычного редактора. В системе Git есть два инструмента для управления рабочим каталогом: команда git clean для удобной отмены изменений в рабочем каталоге и команда git reset
, которую можно вызвать с параметрами --mixed
или --hard
, чтобы сбросить изменения в рабочем каталоге.
Раздел проиндексированных файлов
Команда git add используется для добавления изменений в раздел проиндексированных файлов. Команда git reset
предназначена главным образом для отмены изменений в данном разделе. Команда reset с параметром --mixed
перемещает все ожидающие изменения из раздела проиндексированных файлов обратно в рабочий каталог.
Отмена публичных изменений
При командной работе в удаленных репозиториях необходимо подходить к отмене изменений с особой осторожностью. Команда git reset
, как правило, считается методом локальной отмены. Ее следует использовать для отмены изменений в частной ветке. Она безопасно изолирует удаление коммитов от других веток, которые могут использоваться другими разработчиками. Проблемы возникают, когда команда reset выполняется в общей ветке и затем эта ветка удаленно публикуется с помощью команды git push
. В этом случае Git блокирует выполнение команды push и сообщает, что публикуемая ветка устарела, поскольку в ней отсутствуют коммиты, которые есть в удаленной ветке.
Предпочтительная команда для отмены общей истории коммитов — git revert
. Команда revert безопаснее, чем reset, так как она не удаляет коммиты из общей истории. Команда revert сохраняет отменяемые вами коммиты и создает новый коммит с операцией, обратной последнему коммиту. Этот метод можно безопасно применять в общих распределенных рабочих средах, так как удаленный разработчик может выполнить пул ветки и получить новый коммит, который отменяет его нежелательный коммит.
Резюме
Мы рассмотрели множество общих стратегий отмены изменений в Git. Важно помнить, что отменить изменения в проекте Git можно несколькими способами. Кроме того, здесь были затронуты и более сложные темы, которые подробно рассматриваются на страницах, посвященных соответствующим командам Git. Наиболее часто используемые инструменты для отмены — это команды git checkout, git revert и git reset. Вот несколько ключевых моментов, о которых следует помнить.
- Обычно после коммита внесенные изменения отменить невозможно
- Используйте
git checkout
для переходов и просмотра истории коммитов - Команда
git revert
— лучший инструмент для отмены общих публичных изменений. - Команду
git reset
лучше всего использовать для отмены локальных частных изменений.
Помимо основных команд отмены, мы рассмотрели другие команды Git: git log для поиска потерянных коммитов, git clean для отмены изменений, не подтвержденных коммитами, и git add для изменения индексирования.
Каждая из этих команд имеет собственную подробную документацию. Чтобы узнать больше о той или иной команде, пройдите по соответствующим ссылкам.
Поделитесь этой статьей
Следующая тема
Рекомендуемые статьи
Добавьте эти ресурсы в закладки, чтобы изучить типы команд DevOps или получать регулярные обновления по DevOps в Atlassian.