Простое для понимания объяснение Git Reset | Техрокс | Журнал "Вольт"
Чт. Окт 29th, 2020


Перевод статьи «Объяснение Git Reset – Как спасти день с помощью команды Reset».

«Помогите! Я указал неправильную ветку!» «И снова … Где моя фиксация?» Знакомые ситуации, правда?

Я слышал это много раз. Кто-то звонит мне по имени и просит о помощи, когда что-то пойдет не так с git. И это происходило не только когда я учил студентов, но и при работе с опытными разработчиками.

Со временем я стал чем-то вроде «парня, который понимает Git».

Мы постоянно используем git, и он обычно помогает нам в работе. Но иногда (и гораздо чаще, чем хотелось бы!) Что-то идет не так.

Бывает, что мы отправляем коммит не в ту ветку. Иногда мы теряем часть написанного кода. Или мы можем добавить в коммит что-то еще.

Источник: xkcd.com

Существует множество онлайн-ресурсов по git, и некоторые из них (например, эта статья) посвящены тому, что делать в таких нежелательных ситуациях.

Но мне всегда казалось, что на этих ресурсах не хватает объяснения, почему нужно делать это, а не иначе. Когда дается набор команд, что делает каждая из них? И вообще, как вы пришли к этим командам?

В прошлом посте я рассказал о внутреннем устройстве Git. Хотя это полезно понимать, теории чтения почти всегда недостаточно. Как применить свои знания о внутреннем устройстве git и использовать его для решения возникающих проблем?

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

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

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

Если вы знакомы с этой темой, переходите к следующему разделу. Если вам нужно более подробное объяснение, посмотрите мой предыдущий пост.

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

После того, как мы внесли некоторые изменения, мы хотим отправить их в репозиторий. Репозиторий – это набор коммитов, каждый из которых представляет собой архив того, как выглядело рабочее дерево проекта на момент создания этого архива (на нашей или на чужой).

Создадим файл в рабочем каталоге и запустим команду git status:

Да, git не записывал (не фиксировал) изменения, внесенные в рабочий каталог, непосредственно в репозиторий.

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

Когда мы применяем git add, мы добавляем файлы (или изменения в файлах) в постановку. Давайте попробуем использовать эту команду для только что созданного файла:

Как показывает git status, наш файл подготовлен и готов к фиксации. Да, это еще не часть фиксации. Другими словами, он в настоящее время находится в рабочем каталоге, а также в индексе, но не в репозитории.

Если мы сейчас сделаем git commit, мы создадим фиксацию на основе состояния индекса. Таким образом, новая фиксация (в примере – фиксация 3) будет включать файл, который мы добавили в постановку немного ранее.

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

Когда вы запускаете git commit, текущая главная ветвь начинает указывать на вновь созданный объект фиксации.

Внутренняя работа git reset

Мне нравится представлять git reset как команда, которая обращает процесс, описанный выше (внесение изменений в рабочий каталог, добавление их в индекс, а затем сохранение в репозиторий).

Git reset имеет три режима: --soft, --mixed а также --hard… Я рассматриваю их как три этапа:

  • Этап 1. Обновление HEAD – git reset --soft
  • Этап 2. Обновление индекса – git reset --mixed
  • Этап 3. Обновление рабочего каталога – git reset --hard

Этап 1: обновление HEAD – git reset —soft

В первую очередь, git reset изменяет то, на что указывает HEAD. Если мы выполним git reset --hard HEAD~1, HEAD не будет указывать на master, но HEAD ~ 1. Если вы используете флаг --soft, git reset он остановится на этом.

Возвращаясь к нашему примеру, HEAD будет указывать на фиксацию 2, и, следовательно, new_file.txt не будет частью текущего дерева фиксации. Но он будет частью индекса и рабочего каталога.

Если вы посмотрите git status, мы увидим, что этот файл определенно поставлен, но не зафиксирован.

Другими словами, мы вернули процесс на тот этап, где уже применили git addно еще не подал заявку git commit

Этап 2. Обновление индекса – git reset –mixed

Если мы используем git reset --mixed HEAD~1, git не остановится на обновлении того, на что указывает HEAD. Кроме того, индекс также будет обновлен (до состояния уже обновленной HEAD).

В нашем примере это означает, что индекс будет в той же форме, что и коммит 2:

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

Этап 3. Обновление рабочего каталога – git reset –hard

Если вы используете git reset  -- hard HEAD~1, затем после перемещения указателя HEAD (независимо от того, на что он указывал ранее) на HEAD ~ 1, а также обновления индекса до (уже обновленного) HEAD, git пойдет еще дальше и обновит рабочий каталог до состояния индекса.

В нашем примере это означает, что рабочий каталог будет переведен в состояние индекса, которое уже находится в состоянии фиксации 2:

Собственно, мы вернули весь процесс на этап до создания файла my_file.txt.

Применяем наши знания в реальных сценариях

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

1. Ой! Я совершил что-то по ошибке

Рассмотрим следующий сценарий. Мы создали файл со строкой «Это очень важно», отправили его на подготовку, а затем на фиксацию.

А потом – ах! – обнаружил, что в нашем предложении есть опечатка.

Что ж, теперь мы знаем, что это легко исправить. Мы можем отменить нашу последнюю фиксацию и вернуть файл в рабочий каталог, используя git reset --mixed HEAD~1… Теперь моно отредактировать содержимое файла и снова зафиксировать.

Совет… В этом конкретном случае мы также можем использовать git commit --amendкак описано здесь.

2. Ой! Я сделал фиксацию не в той ветке, и мне нужны эти изменения в новой ветке

Это случилось со всеми нами. Что-то сделал, совершил …

О нет, мы сделали фиксацию в главной ветке, и нам пришлось создать новую, а затем сделать запрос на перенос.

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

Собственно, от желаемого состояния нас отделяют три изменения.

  1. Ветвь new должен указывать на нашу недавно добавленную фиксацию.
  2. Ветвь master должен указывать на предыдущую фиксацию.
  3. HEAD должен указывать на новый.

Мы можем достичь желаемого положения за три шага:

Для начала нужно сделать ветку new указал на недавно добавленную фиксацию. Этого можно добиться с помощью команды git branch new… Таким образом, мы достигаем следующего состояния:

Во-вторых, вам нужно указать master на предыдущий коммит (другими словами, HEAD ~ 1). Этого можно добиться с помощью команды git reset --hard HEAD~1… Таким образом, мы достигаем следующего состояния:

Наконец, мы хотели бы быть в новой ветке, т.е. сделать так, чтобы HEAD указывал на new… Этого легко добиться, запустив команду git checkout new

Всего:

  • git branch new
  • git reset --hard HEAD~1
  • git checkout new

3. Ой! Я отправил коммит не в ту ветку, и он мне нужен в другой (уже существующей) ветке

В этом случае проходим те же шаги, что и в предыдущем сценарии. Мы поработали и зафиксировали изменения …

О нет, мы отправили коммит в ветку master, но его нужно было отправить совсем в другой.

Снова нарисуем текущую и желаемую позицию:

Опять же, у нас есть три отличия.

Мы хотим, чтобы самая последняя фиксация была в существующей ветке. Поскольку на этот коммит в настоящее время указывает master, мы можем попросить git получить последний коммит из ветки master и примените его к ветке existing:

  • git checkout existing– перейти в ветку existing,
  • git cherry-pick master– применение последнего коммита в главной ветке к текущей ветке (existing).

Теперь наша позиция такова:

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

  • git checkout master– изменить активную ветку на master,
  • git reset --hard HEAD~1– теперь мы вернулись к исходному состоянию этой ветки.

Таким образом, мы достигли желаемой позиции:

Результат

В этой статье мы рассмотрели, как git resetа также разобрали три разных режима этой команды: --soft, --mixed а также --hard

Мы также применили наши новые знания для решения жизненных проблем.

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



Source link

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *