Отмена: restore, revert и reset решают разные задачи
Сначала назовите, что ошибочно
Вы случайно подготовили файл, сделали локальный неудачный коммит или уже опубликовали неверное правило. Эти ситуации требуют разных действий. Слово «откатить» не сообщает, нужно ли сохранить рабочий текст, сдвинуть ветку или создать исправление поверх общей истории. Перед командой проверьте status, текущую ветку и известность коммита другим людям.
restore восстанавливает выбранные пути в рабочем дереве или индексе. revert создаёт новый коммит с обратным изменением выбранной записи. reset в форме с коммитом перемещает текущую ветку и в зависимости от режима обновляет индекс и рабочие файлы. Restore, revert и reset нельзя использовать как взаимозаменяемые сокращения.
Для уже общей истории обычно нужно видимое исправление через revert, чтобы существующие записи и работа других авторов оставались продолжением одной линии. Для неопубликованного черновика допустимо изменить свою локальную историю. Даже тогда сначала сохраните нужное состояние отдельной ссылкой, если собираетесь сравнивать варианты.
Уберите файл из подготовки, сохранив текст
lab=$(mktemp -d "${TMPDIR:-/tmp}/git-09.XXXXXX")
cd "$lab"
git init -b main
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
git config commit.gpgsign false
printf 'queue=16\n' > settings.txt
git add settings.txt
git commit -m "Set capacity 16"
printf 'queue=32\n' > settings.txt
git add settings.txt
git restore --staged -- settings.txt
git diff --cached
git diff -- settings.txtCached diff теперь пуст, а рабочий diff содержит изменение до 32. Восстановление индекса из HEAD сняло подготовку, не уничтожив правку. Обычный git restore -- settings.txt затем заменит рабочий файл содержимым индекса, то есть вернёт 16. Выполните это только над учебной правкой в этой временной папке, которую вы сознательно готовы потерять. Посмотрите файл после команды.
Незакоммиченные правки не становятся резервной копией автоматически. История и reflog помогают с коммитами и движением ссылок, но не обещают восстановить каждый затёртый рабочий текст. Если содержимое важно, сохраните его отдельно до восстановления.
Отмените коммит новым коммитом
printf 'queue=32\n' > settings.txt
git add settings.txt
git commit -m "Raise capacity without checking requirement"
bad=$(git rev-parse HEAD)
git revert --no-edit "$bad"
cat settings.txt
git log --onelineЗначение снова 16, но в log остаются ошибочная запись и новая запись отмены. Это честная история принятого исправления. Revert может конфликтовать с поздними изменениями: обратный diff не всегда применим автоматически. При остановке разрешайте конфликт и используйте git revert --continue либо отменяйте операцию через git revert --abort.
Мы отменяем простой обычный коммит. Revert merge-коммита требует выбора mainline-родителя и влияет на будущие слияния; не выводите его поведение из этого опыта и не выбирайте -m наугад.
Сравните режимы reset на своём черновике
printf 'workers=2\n' > workers.txt
git add workers.txt
git commit -m "Draft worker setting"
git branch saved-draft
git reset --soft HEAD~1
git status --short
git reset --mixed HEAD
git status --short
git show saved-draft:workers.txt
git reflog -5Soft переместил main к предыдущему коммиту, оставив индекс и рабочий файл черновика. Mixed обновил индекс под текущий HEAD, но сохранил рабочее содержимое. Поэтому новый workers.txt стал untracked. Для уже существовавшего файла аналогичная правка была бы изменением tracked пути.
Hard обновляет и рабочее дерево; он способен уничтожить несохранённые изменения и убрать мешающие неотслеживаемые пути. В курсе он не нужен для исправления общей истории. Reflog показывает локальные движения ссылок; его записи имеют ограниченный срок хранения и не передаются автоматически через push. Saved-draft даёт явное имя старому коммиту, пока вы исследуете результат.
Разделите сохранение текста и сохранение линии
Коллега просит убрать последний неопубликованный commit, но оставить его содержимое для дальнейшей правки. Удалять файл не требуется: soft или mixed могут оставить рабочий текст, различаясь подготовкой. Если требуется только снять один путь из индекса, перемещать ветку вообще не нужно. Сначала сформулируйте требуемое состояние всех трёх областей и лишь затем выберите действие.
Теперь та же просьба относится к опубликованному коммиту, поверх которого другой автор уже работает. Локальное перемещение вашего main не создаст общего исправления; оно лишь изменит вашу ссылку. Новый revert продолжает известную линию и позволяет другим получить изменение обычным обменом. При этом обратная правка может затронуть нужный поздний текст, поэтому проверяйте содержание.
Для своей таблицы добавьте колонку «что смогут получить другие через обычный fetch и интеграцию». Это делает границу личной и общей истории наблюдаемой. Умение вернуть конкретный файл полезно, но не равняется умению согласованно исправить общий проект. После команды status должен соответствовать вашему намерению, а не просто стать пустым.
Самостоятельная лаборатория
Практика — 45–60 минут. Подготовьте правку, снимите подготовку и подтвердите сохранение рабочего текста. Затем сознательно отбросьте только эту учебную правку. Создайте неверный коммит и revert, покажите оба в log. На отдельном локальном черновике сравните soft и mixed, заранее создав ветку сохранения.
Для каждой операции запишите четыре значения: текущая ветка, HEAD, индекс и рабочий файл. Объясните, какие изменились. Не используйте reset над общей bare-веткой из предыдущей главы. Цель — разобраться в областях, а не получить одинаковое число файлов любой ценой.
Критерии готовности
Вы выбираете команду по ситуации, а не по слову «откат». Снятие подготовки сохраняет текст; revert сохраняет ошибочную запись в истории; soft и mixed оставляют разные состояния индекса. Старый черновик читается через saved-draft. Если после restore текст исчез, вы выбрали восстановление рабочего дерева вместо снятия подготовки.
Разбор
Перемещение main не переписывает содержимое старого объекта: меняется достижимость коммита из выбранной ссылки. Reflog и saved-draft делают это наблюдаемым. Следующий урок — rebase только своей неопубликованной линии, где новые записи получат другие идентификаторы.