Git Reset и Reflog

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Добро пожаловать, путешественник по времени Git! Сегодня мы рассмотрим две мощные функции Git, которые дадут вам беспрецедентный контроль над историей вашего репозитория: git reset и git reflog. Эти инструменты похожи на продвинутые контролы вашей машины времени Git, позволяющие перемещаться между различными состояниями вашего проекта и даже восстанавливать "потерянную" работу.

Команда git reset - это универсальный инструмент, который может помочь отменить изменения, убрать файлы из индекса и даже переписать историю коммитов. Однако, с большой силой приходит большая ответственность, и git reset может показаться немного пугающим для новичков. Именно здесь на помощь приходит git reflog - это как страховой мешок, который отслеживает все изменения, которые вы вносите в ссылки (refs) вашего репозитория (например, вершины веток), позволяя восстановиться даже после самых радикальных сбросов.

В этом LabEx вы научитесь:

  1. Мягкому сбросу (Soft Reset): перемещению HEAD без изменения рабочей директории или индекса
  2. Смешанному сбросу (Mixed Reset): удалению изменений из индекса, сохраняя модификации в рабочей директории
  3. Жесткому сбросу (Hard Reset): полному удалению изменений
  4. Использованию reflog для восстановления после "разрушительных" операций
  5. Сбросу на основе времени: перемещению вашего репозитория в состояние, соответствующее определенной точке времени

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

Давайте приступим и начнем овладевать git reset и reflog!


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("Git")) -.-> git/SetupandConfigGroup(["Setup and Config"]) git(("Git")) -.-> git/BasicOperationsGroup(["Basic Operations"]) git(("Git")) -.-> git/DataManagementGroup(["Data Management"]) git(("Git")) -.-> git/BranchManagementGroup(["Branch Management"]) git/SetupandConfigGroup -.-> git/init("Initialize Repo") git/BasicOperationsGroup -.-> git/status("Check Status") git/BasicOperationsGroup -.-> git/commit("Create Commit") git/DataManagementGroup -.-> git/reset("Undo Changes") git/BranchManagementGroup -.-> git/log("Show Commits") git/BranchManagementGroup -.-> git/reflog("Log Ref Changes") subgraph Lab Skills git/init -.-> lab-387491{{"Git Reset и Reflog"}} git/status -.-> lab-387491{{"Git Reset и Reflog"}} git/commit -.-> lab-387491{{"Git Reset и Reflog"}} git/reset -.-> lab-387491{{"Git Reset и Reflog"}} git/log -.-> lab-387491{{"Git Reset и Reflog"}} git/reflog -.-> lab-387491{{"Git Reset и Reflog"}} end

Настройка рабочего пространства

Перед тем, как мы начнем использовать команды сброса (reset) и журнал ссылок (reflog), давайте создадим рабочее пространство с некоторой историей коммитов для экспериментов. Мы создадим новую директорию, инициализируем Git-репозиторий и добавим несколько файлов с множеством коммитов.

Откройте терминал и введите следующие команды:

cd ~/project
mkdir git-reset-lab
cd git-reset-lab
git init

Теперь давайте создадим несколько файлов и сделаем серию коммитов, скопировав и вставив следующие команды в терминал:

echo "## Git Reset and Reflog Lab" > README.md
git add README.md
git commit -m "Initial commit"

echo "function add(a, b) { return a + b; }" > math.js
git add math.js
git commit -m "Add addition function"

echo "function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtraction function"

echo "function multiply(a, b) { return a * b; }" >> math.js
git add math.js
git commit -m "Add multiplication function"

Разберем, что мы только что сделали:

  1. Мы создали файл README и сделали первый коммит.
  2. Мы создали JavaScript-файл с функцией сложения и закоммитили его.
  3. Мы добавили функцию вычитания в тот же файл и закоммитили изменения.
  4. Наконец, мы добавили функцию умножения и закоммитили ее.

Теперь у нас есть репозиторий с некоторой историей, на которой можно проводить эксперименты!

Мягкий сброс (Soft Reset): перемещение HEAD

Первый тип сброса, который мы рассмотрим, это "мягкий" сброс (soft reset). Мягкий сброс перемещает HEAD (и текущую ветку) на другой коммит, но не изменяет индекс (staging area) или рабочую директорию. Это полезно, когда вы хотите "отменить" некоторые коммиты, но сохранить все изменения для нового коммита.

Давайте попробуем мягкий сброс:

git reset --soft HEAD~1

Эта команда перемещает HEAD на один коммит назад. ~1 означает "один коммит до текущего". Вы можете использовать ~2, ~3 и т.д., чтобы вернуться на несколько коммитов назад.

Теперь, если вы выполните git status, вы увидите, что изменения из последнего коммита находятся в индексе и готовы к повторному коммиту. Файлы в вашей рабочей директории не изменились.

Это полезно в сценариях, когда вы хотите "склеить" (squash) несколько последних коммитов в один. Вы можете выполнить мягкий сброс на несколько коммитов назад, а затем сделать новый коммит со всеми этими изменениями.

Давайте закоммитим эти изменения еще раз с новым сообщением:

git commit -m "Add subtraction and multiplication functions"

Помните, что хотя мягкий сброс обычно безопасен (так как он не удаляет никаких изменений), он все же переписывает историю. Если вы уже отправили (push) исходные коммиты, вам нужно будет выполнить принудительную отправку (force push), чтобы обновить удаленную ветку, что может привести к проблемам для коллег. Всегда общайтесь с командой перед переписыванием общей истории!

Смешанный сброс (Mixed Reset): удаление изменений из индекса

Следующий тип сброса, который мы рассмотрим, это "смешанный" сброс (mixed reset). Фактически, это режим по умолчанию для команды git reset, если вы не укажете флаг. Смешанный сброс перемещает HEAD и обновляет индекс (staging area) соответственно, но не затрагивает рабочую директорию.

Давайте внесем некоторые изменения и добавим их в индекс:

echo "function divide(a, b) { return a / b; }" >> math.js
git add math.js

Теперь предположим, что мы изменили свое мнение и пока не хотим добавлять эти изменения в индекс. Мы можем использовать смешанный сброс:

git reset HEAD

Эта команда удаляет наши изменения из индекса, но сохраняет их в рабочей директории. Если вы выполните git status сейчас, вы увидите, что файл math.js был изменен, но изменения не добавлены в индекс.

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

Помните, в отличие от мягкого сброса (soft reset), смешанный сброс действительно изменяет индекс. Однако он все еще безопасен в том смысле, что не удаляет никакой вашей работы - все изменения по-прежнему находятся в вашей рабочей директории.

Жесткий сброс (Hard Reset): удаление изменений

Третий и самый радикальный тип сброса - это "жесткий" сброс (hard reset). Жесткий сброс перемещает HEAD, обновляет индекс (staging area) и рабочую директорию так, чтобы они соответствовали выбранному коммиту. Это означает, что все изменения с момента этого коммита будут удалены.

Давайте попробуем жесткий сброс:

git add math.js
git commit -m "Add division function"
git status
git reset --hard HEAD~1

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

Если вы теперь посмотрите на файл math.js, вы увидите, что функция деления исчезла. Казалось бы, мы ее и не писали.

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

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

Использование журнала ссылок (reflog) для восстановления потерянных коммитов

Теперь, что делать, если вы понимаете, что не хотели удалить функцию деления? Именно здесь на помощь приходит команда git reflog. Журнал ссылок (reflog) - это журнал всех мест, где находился HEAD в вашем локальном репозитории. Это как супер - история, которая даже записывает команды, переписывающие историю, такие как сброс (reset).

Давайте посмотрим на журнал ссылок:

git reflog

Вы должны увидеть список всех недавних действий, которые вы выполнили, включая сбросы. Каждая запись имеет идентификатор HEAD@{n}.

Для восстановления потерянного коммита вы можете сбросить состояние до жесткого сброса:

git reset --hard HEAD@{1}

Это сбросит состояние HEAD до состояния перед последним действием (которым был жесткий сброс).

Проверьте файл math.js сейчас, и вы должны увидеть, что ваша функция деления вернулась!

cat math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function multiply(a, b) { return a * b; }
function divide(a, b) { return a / b; }

Журнал ссылок (reflog) - это мощная страховка, позволяющая восстановиться от почти любого Git - аварии. Однако, помните, что он локален для вашего компьютера и временен (записи обычно хранятся от 30 до 90 дней). Он не заменяет регулярные резервные копии или отправку вашей работы в удаленный репозиторий.

Сброс на основе времени

Git также позволяет сбросить состояние репозитория на то, которое было в определенный момент времени. Это может быть полезно, если вы примерно помните, когда репозиторий был в состоянии, к которому вы хотите вернуться.

Давайте попробуем сброс на основе времени:

git reset --hard master@{"1 hour ago"}

Эта команда сбросит ваш репозиторий к состоянию, которое было час назад. Вы можете использовать различные описания времени, такие как "вчера", "2 дня назад", "3 минуты назад" и т.д.

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

Помните, что вы всегда можете использовать журнал ссылок (reflog), чтобы отменить сброс на основе времени, если он не дал ожидаемого результата.

Итоги

Поздравляем, мастер времени Git! Вы только что освоили некоторые из самых мощных и потенциально опасных команд Git. Давайте повторим ключевые концепции, которые мы рассмотрели:

  1. Мягкий сброс (Soft Reset): Перемещает HEAD без изменения индекса (staging area) или рабочей директории. Полезен для объединения коммитов (squashing commits).
  2. Смешанный сброс (Mixed Reset): Перемещает HEAD и обновляет индекс, но не затрагивает рабочую директорию. Отлично подходит для удаления изменений из индекса.
  3. Жесткий сброс (Hard Reset): Перемещает HEAD, обновляет индекс и рабочую директорию. Мощный, но потенциально опасный.
  4. Журнал ссылок (Reflog): Страховой мешок, который записывает все изменения HEAD, позволяя восстановиться от почти любой Git - аварии.
  5. Сброс на основе времени (Time - based Resets): Позволяет сбросить репозиторий к состоянию, которое было в определенный момент времени.

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

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

Счастливых сбросов, и пусть ваша Git - история всегда будет чистой и значащей!