Урок курса

Выбрать исправление по состоянию проекта

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.

Независимые состояния и цели.

  1. В case1 добавьте в README.md строку «Версия: 1», сохраните и выполните git add README.md. Цель: снять только staged-изменение, сохранив рабочий текст. Других правок нет.
  2. В case2 добавьте ту же строку только в рабочем файле; индекс равен HEAD, staged diff пуст. Просмотрите правку и решите отказаться от неё. Цель: вернуть README к исходному тексту.
  3. В case3 измените статус README на «готово», выполните git add README.md и git commit -m "Обновить статус". Создайте author.txt с текстом «Автор: команда», пока не добавляя его. Цель: дополнить последний собственный неопубликованный коммит забытым author.txt, сохранив сообщение. Ничего из case3 не отправляйте в remote; других правок нет.
  4. В prepared/revert_history прочитайте git log --oneline и git show для найденного обычного коммита «Обновить контакт». Он не последний; последующий коммит добавляет navigation.md. Цель: отменить только изменение контакта новым коммитом, сохранив navigation.md и прежнюю историю. Исходное состояние чистое, обратная правка применяется без конфликта.
  5. В 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, сохранив дополнительный текст для отдельного решения. Как разумно продолжить?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку