Урок курса
Выбрать исправление по состоянию проекта
Git и GitHub с нуля: бесплатный курс с практикойОдно и то же слово «modified» в выводе status не определяет способ исправления. Выбор зависит от того, где находится изменение — в рабочем файле, индексе или истории, — и что требуется сохранить. Отказ от незавершённого merge дополнительно зависит от состояния слияния и правок, сделанных во время него.
Критерии выбора команды восстановления по состоянию трёх областей
Во всех пяти опытах HEAD существует, README.md уже присутствует в HEAD и текущем индексе. Других правок, кроме явно заданных, нет. Выбор команды определяется состоянием репозитория и целью восстановления.
Вопрос 1. Есть ли незавершённое слияние?
Если git status сообщает о конфликте слияния и вы хотите полностью от него отказаться — git merge --abort. В заданном опыте merge начат из чистого состояния, после конфликта файлы не менялись; это позволяет проверить возврат к исходным файлам и индексу.
Вопрос 2. Находится ли изменение только в рабочей папке — или уже в индексе? Здесь помогает пара команд:
git diff # рабочая папка vs индекс
git diff --staged # индекс vs HEAD
Если git diff --staged показывает изменение — файл добавлен в индекс. Если git diff --staged пуст, а git diff показывает изменение — правка только в рабочей папке.
- Изменение в индексе, и нужно его снять, но сохранить рабочий текст →
git restore --staged <файл>. - Изменение только в рабочей папке, и нужно отказаться от правки совсем →
git restore <файл>.
Важно не путать эти два случая: git restore без --staged перезаписывает рабочий файл версией из индекса и прежний текст не сохраняет.
Вопрос 3. Нужно ли изменить уже созданный коммит? Если изменение уже зафиксировано коммитом, вопросы про рабочую папку и индекс больше не определяют выбор — важно другое.
Вопрос 3а. Это последний коммит, и он ещё не опубликован?
Если коммит существует только локально (не отправлялся в remote) и нужно либо добавить в него забытый файл, либо исправить сообщение → git commit --amend. Перед этим нужно убедиться, что индекс содержит именно тот снимок, который должен оказаться в исправленном коммите.
Вопрос 3б. Нужно отменить коммит не из вершины истории, сохранив последующие коммиты?
Если коммит не последний или уже опубликован, и требуется отменить его изменение новым коммитом, не трогая историю → git revert --no-edit <ID>. Эта команда создаёт новый коммит с обратной правкой и не удаляет ничего из прежней истории.
Схема выбора выглядит так:
Есть незавершённое слияние? Да → merge --abort Нет ↓ Изменение в индексе (diff --staged не пуст)? Да, нужно снять из индекса, сохранив рабочий текст → restore --staged Нет, правка только в рабочей папке, нужно отказаться → restore Изменение уже в коммите? Последний, неопубликованный, нужно дополнить или исправить → amend Не последний или опубликован, нужно отменить через новый коммит → revert
Один диагностический вопрос про «modified» ничего не решает: нужно знать, в какой именно области живёт изменение и что с ним требуется сделать.
Применение выбора к пяти независимым состояниям проекта
Для пяти независимых опытов распакуйте весь выданный git_practice.zip в новую пустую папку через файловый менеджер. Откройте терминал в этой папке и перейдите в корень распаковки:
cd "git_practice"
git clone remote/shared.git case1
git clone remote/shared.git case2
git clone remote/shared.git case3
case1, case2 и case3 создаются этими командами: готовых папок с такими именами в ZIP нет. Каждый клон начинается на main с одним коммитом и отслеживаемым README.md: контакт «команда», статус «черновик».
До создания коммитов задайте оба поля автора в каждом репозитории:
cd case1
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../case2
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../case3
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../prepared/revert_history
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../merge_conflict
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../..
Обе prepared-папки уже содержат .git, повторный init не нужен. Все пять репозиториев начинают на main с чистыми рабочей папкой и индексом; в prepared/merge_conflict merge ещё не запущен. Настройки автора из архива явно заменены учебными значениями. Ничего из этих опытов не отправляйте в remote. Перед восстановлением в каждом репозитории сохраните git status, оба diff, git log --oneline и содержимое файлов, чтобы сравнить результат с исходным состоянием. Результат одного опыта не служит подготовкой другого.
Состояние 1 (case1): README изменён и добавлен в индекс, других правок нет. Цель — снять staged-изменение, сохранив рабочий текст.
Из git_practice войдите в первый клон:
cd case1
git status
Убедитесь в чистом исходном состоянии. В редакторе добавьте в конец README.md строку Версия: 1, сохраните файл и подготовьте правку:
git add README.md
git diff --staged покажет строку +Версия: 1. Это значит изменение уже в индексе. Слияния нет, новый коммит для этой правки ещё не создан — вопросы про amend и revert не актуальны. Нужно снять из индекса, но оставить правку в файле.
Выбор: git restore --staged README.md
- Рабочий файл: строка
Версия: 1остаётся. - Индекс: возвращается к версии HEAD, staged-изменение снято.
- История: не меняется — нового коммита не создано.
Состояние 2 (case2): README изменён только в рабочей папке, индекс совпадает с HEAD. Цель — отказаться от правки совсем.
После опыта в case1 перейдите во второй независимый клон:
cd ../case2
git status
Он должен быть чистым. В редакторе добавьте в конец README.md строку Версия: 1 и сохраните файл; git add не выполняйте. Других правок нет.
git diff --staged пуст. git diff показывает +Версия: 1 — правка только в рабочей папке. Слияния нет, эта правка не закоммичена. Нужно отбросить правку.
Выбор: git restore README.md
- Рабочий файл: перезаписывается версией из индекса (= HEAD). Строка
Версия: 1исчезает из рабочего файла; команда автоматически не сохраняет прежний текст. - Индекс: не меняется — он уже совпадал с HEAD.
- История: не меняется.
Если рабочая правка не была отдельно сохранена, Git не гарантирует её восстановление после этой команды. Перед действием убедитесь, что она действительно не нужна.
Состояние 3 (case3): последний коммит «Обновить статус» создан локально, не отправлен в remote. Забыт файл author.txt. Цель — дополнить коммит файлом, сохранив сообщение.
Перейдите из case2 в третий независимый клон:
cd ../case3
git status
При чистом исходном состоянии замените в редакторе только Статус: черновик на Статус: готово, сохранив заголовок и контакт. Создайте собственный коммит:
git add README.md
git commit -m "Обновить статус"
В редакторе создайте author.txt с единственной строкой Автор: команда и переводом строки в конце. Пока не добавляйте его; других правок нет. Сохраните текущий log перед amend.
Нет незавершённого слияния. Изменение статуса уже в коммите. Коммит последний и неопубликованный — подходит amend. Перед командой нужно добавить author.txt в индекс:
git add author.txt
git commit --amend --no-edit
- Рабочий файл: author.txt остаётся на месте.
- Индекс: снимок с author.txt теперь является содержимым нового коммита.
- История: число коммитов прежнее, сообщение прежнее, но ID вершины другой — старый коммит заменён новым объектом. Это не «редактирование» коммита, а создание нового на его месте.
Состояние 4 (prepared/revert_history): коммит «Обновить контакт» не последний — после него есть коммит, добавляющий navigation.md. Цель — отменить только изменение контакта, сохранив navigation.md и всю прежнюю историю.
Из case3 перейдите в готовую независимую папку:
cd ../prepared/revert_history
git status
При чистом исходном состоянии откройте README.md и navigation.md и сохраните их содержимое для сравнения.
Нет незавершённого слияния. Изменение в коммите, но коммит не последний — amend неприменим. Нужно отменить конкретный коммит, не трогая последующие. Сначала узнаём ID:
git log --oneline
Находим хэш коммита «Обновить контакт» и выполняем:
git revert --no-edit <ID коммита «Обновить контакт»>
- Рабочий файл: контакт возвращён к исходному тексту.
- Индекс: чист, отменяющий коммит уже создан.
- История: удлиняется — прежние коммиты сохранены, поверх них добавлен новый отменяющий. navigation.md никуда не делся, потому что revert применяет обратное изменение только к тому, что принёс конкретный коммит.
Состояние 5 (prepared/merge_conflict): слияние с веткой feature начато из чистого состояния, возник конфликт в одной строке README. После конфликта файлы не редактировались и не добавлялись. Цель — полностью отказаться от слияния.
Перейдите из revert_history в соседний готовый репозиторий:
cd ../merge_conflict
git status
git diff
git diff --staged
git log --oneline
Статус должен быть чистым, оба diff пусты. Откройте README.md в редакторе и сохраните его исходное содержимое для сравнения. Теперь запустите конфликтное слияние:
git merge feature
git status
После сообщения о конфликте не редактируйте и не добавляйте файлы. git status сообщает о незавершённом слиянии — это первый диагностический вопрос, и ответ на него однозначен.
Выбор: git merge --abort
- Рабочий файл: README возвращается к версии до
git merge. - Индекс: возвращается к состоянию до merge — конфликтные маркеры исчезают.
- История: merge-коммит не создавался, история не меняется.
Abort работает корректно именно потому, что до merge состояние было чистым и после конфликта никаких посторонних правок не вносилось. После дополнительных правок нельзя гарантировать выполнение abort: команда может отказать, если восстановление перезаписало бы рабочие изменения, либо при успешном выполнении отбросить правки разрешения. Если нужный текст появился уже во время слияния, сохраните его отдельно в редакторе до попытки abort и проверьте фактический результат. Гарантия этого опыта относится только к объявленным условиям.
Проверка результата через status, diff и log после каждого восстановления
Чистый git status — плохой единственный критерий. Для состояний 2–5 он покажет «nothing to commit, working tree clean», но не скажет, тот ли текст в файле, та ли история в log и тот ли снимок в последнем коммите. Для состояния 1 status вообще будет показывать рабочую правку — и это правильный результат, а не ошибка.
Следующие проверки выполняются в соответствующих независимых репозиториях. После пятого опыта терминал находится в git_practice/prepared/merge_conflict; переходы ниже переводят его по тем же пяти папкам. Проверки читают состояние и не переносят результаты между опытами.
Состояние 1 — после git restore --staged README.md
cd ../../case1
git status # Changes not staged: README.md — правка видна в рабочей папке
git diff # показывает +Версия: 1
git diff --staged # пуст
git log --oneline # история не изменилась
Если git diff --staged не пуст — staged-изменение не снято. Если git diff пуст — рабочая правка потеряна (была применена неправильная команда).
Состояние 2 — после git restore README.md
cd ../case2
git status # nothing to commit, working tree clean
git diff # пуст
git diff --staged # пуст
Кроме того, нужно открыть README.md и убедиться, что строки Версия: 1 нет. Чистый status при наличии этой строки в файле означал бы, что правка была закоммичена — совсем другой исход.
Состояние 3 — после git commit --amend --no-edit
cd ../case3
git log --oneline # число коммитов прежнее; ID вершины отличается от исходного
git show # сведения о последнем коммите и его diff к родителю
git show HEAD:README.md # полное содержимое README в последнем снимке
git show HEAD:author.txt # полное содержимое author.txt в последнем снимке
git status # чист
Полный README из git show HEAD:README.md должен сохранить заголовок и контакт и содержать Статус: готово; git show HEAD:author.txt должен вывести Автор: команда. Если второго пути нет в HEAD — файл не попал в исправленный снимок. Если число коммитов увеличилось — был создан обычный коммит вместо amend. Если ID вершины не изменился — amend не выполнился или файл уже был в предыдущем снимке.
Состояние 4 — после git revert --no-edit <ID>
cd ../prepared/revert_history
git log --oneline # история длиннее на один коммит; исходные коммиты сохранены
git show HEAD # показывает обратную правку: контакт возвращён
git status # чист
Дополнительно стоит проверить, что navigation.md присутствует в рабочей папке и в git show для коммита, который его добавил. Если navigation.md пропал — что-то пошло не так с выбором ID для revert.
Состояние 5 — после git merge --abort
cd ../merge_conflict
git status # чист; нет упоминания незавершённого слияния
git diff # пуст
git diff --staged # пуст
git log --oneline # совпадает с тем, что было до git merge
Открыть README.md и сравнить с сохранённым перед merge содержимым. Если в файле остались конфликтные маркеры <<<<<<< — abort не выполнился или был прерван. Если log содержит merge-коммит — слияние успело завершиться до abort, и это уже другая ситуация.
Общий принцип: для состояний, где должен был появиться новый коммит (состояние 4), доказательство — в git log. Для состояний, где история не должна меняться (1, 2, 5), доказательство — в содержимом файла и обоих diff. Для состояния 3 нужны оба: и log (ID изменился, число прежнее), и git show HEAD:<путь> (нужные файлы содержат требуемый полный текст).
Задача 1
Три независимые цели: А — снять staged-изменение tracked README, оставив рабочую правку; Б — отбросить рабочую правку tracked README при индексе, равном HEAD; В — отказаться от незавершённого merge, начатого чистым, без посторонних изменений. Какая строка верно выбирает средства?
Задача 2
HEAD содержит А, индекс tracked README содержит Б, рабочий файл содержит В. Других изменений нет. Нужно сохранить В на диске, убрав только подготовленное отличие Б от А. Выполнили restore --staged README.md. Какая тройка рабочий/индекс/HEAD и проверки ожидаются?
Задача 3
Письменное объяснение:
Коллега предлагает всегда заменять плохой коммит через amend. На столе два независимых случая: (A) нужно исправить сообщение последнего собственного коммита, который ещё никуда не отправлялся; индекс равен HEAD; (B) нужно отменить обычный непоследний коммит общей истории, сохранив последующий независимый коммит; обратная правка применяется без конфликта. Обоснуйте выбор amend или revert в каждом случае, ожидаемое изменение текущей истории и проверку результата. Поясните, почему отсутствие прежнего ID на вершине после amend не доказывает стирание объекта.
Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.
Контрольный случай:
Сравните два независимых случая: А — действительно исправлено сообщение последнего собственного неопубликованного коммита через amend при чистом индексе; Б — обратная правка обычного опубликованного непоследнего коммита выполнена revert без конфликта. Как меняется текущая история?
Задача 4
Самостоятельная практика:
Настоящая локальная практика Git на своём компьютере. Площадка не запускает команды и не проверяет репозиторий. Выполните пять независимых опытов: результат одного не служит подготовкой следующего.
Подготовка. Распакуйте выданный git_practice.zip в новую пустую папку и перейдите в git_practice. В remote/shared.git ветка main, один коммит, README.md с контактом «команда» и статусом «черновик». Создайте три независимых клона:
git clone remote/shared.git case1
git clone remote/shared.git case2
git clone remote/shared.git case3
cd case1
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../case2
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../case3
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../prepared/revert_history
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../merge_conflict
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
cd ../..
Prepared-папки уже содержат .git; init в них не нужен. Все пять репозиториев начинают на main с чистыми рабочей папкой и индексом. case1–3 имеют существующий HEAD и tracked README.md.
Независимые состояния и цели.
- В case1 добавьте в README.md строку «Версия: 1», сохраните и выполните git add README.md. Цель: снять только staged-изменение, сохранив рабочий текст. Других правок нет.
- В case2 добавьте ту же строку только в рабочем файле; индекс равен HEAD, staged diff пуст. Просмотрите правку и решите отказаться от неё. Цель: вернуть README к исходному тексту.
- В case3 измените статус README на «готово», выполните git add README.md и git commit -m "Обновить статус". Создайте author.txt с текстом «Автор: команда», пока не добавляя его. Цель: дополнить последний собственный неопубликованный коммит забытым author.txt, сохранив сообщение. Ничего из case3 не отправляйте в remote; других правок нет.
- В prepared/revert_history прочитайте git log --oneline и git show для найденного обычного коммита «Обновить контакт». Он не последний; последующий коммит добавляет navigation.md. Цель: отменить только изменение контакта новым коммитом, сохранив navigation.md и прежнюю историю. Исходное состояние чистое, обратная правка применяется без конфликта.
- В prepared/merge_conflict сохраните исходный status, diff, staged diff, log и README; выполните git merge feature, чтобы получить конфликт двух версий одной строки статуса. До merge состояние чистое. После возникновения конфликта не редактируйте и не добавляйте файлы. Цель: полностью отказаться от незавершённого слияния.
В каждом случае самостоятельно выберите и выполните подходящий изученный способ. Запишите, почему выбран он и что должно сохраниться либо быть отброшено в рабочем файле, индексе и истории.
Подтверждение. Представьте исходные и итоговые git status, git diff, git diff --staged, git log --oneline и наблюдение содержимого файлов. Для 1 правка осталась в рабочем diff, staged diff пуст, история прежняя; для 2 файл равен исходному и оба diff пусты; для 3 последний коммит содержит author.txt, сообщение и число коммитов прежние, ID вершины другой (снимок действительно дополнен); для 4 новый отменяющий коммит добавлен поверх прежних, контакт исходный, navigation.md сохранён; для 5 конфликтное состояние исчезло, файл, индекс и история совпадают с сохранённым состоянием перед merge. Для 2–5 итоговый status чист, для 1 рабочая правка остаётся.
Доступные средства: редактор и изученные git clone, git config user.name/user.email, git status, git diff, git diff --staged, git log --oneline, git show, git restore --staged, git restore, git add, git commit -m, git commit --amend -m/--no-edit, git revert --no-edit <ID>, git merge feature, git merge --abort. Не используйте случайные даты или заранее заданные SHA как критерий.
Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.
Контрольный случай:
В другом репозитории merge feature начали из чистой main и получили конфликт README. Автор убрал маркеры, записал «Статус: согласовано» и выполнил git add README.md. Затем дописал в рабочий README строку «Дополнительная нужная правка.», не выполняя add. Попытка git merge --abort завершилась ошибками «Entry README.md not uptodate» и «Could not reset index file»; status всё ещё показывает незавершённое слияние. Требуется отказаться от merge, сохранив дополнительный текст для отдельного решения. Как разумно продолжить?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
