Урок курса
Отказаться от незавершённого слияния
Git и GitHub с нуля: бесплатный курс с практикойВ прошлом уроке мы получали конфликт слияния, читали маркеры и разрешали его вручную. Если merge запущен по ошибке или разрешать конфликт пока не нужно, незавершённое слияние можно прервать и вернуться к исходному состоянию.
Состояние MERGING: промежуточная точка между двумя коммитами
Когда git merge feature обнаруживает конфликт, он не останавливается молча — он переводит репозиторий в особое промежуточное состояние, которое Git называет MERGING. В этом состоянии произошло следующее:
- В конфликтующий файл записаны маркеры
<<<<<<<,=======,>>>>>>>. - Индекс хранит оба варианта изменения (от
HEADи от сливаемой ветки). - Merge-коммит ещё не создан — история не изменилась.
git status в этом состоянии явно сообщает о произошедшем:
You have unmerged paths.
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README.md
Слово both modified означает, что обе ветки затронули один участок файла, и Git не смог выбрать победителя автоматически. Git сам подсказывает два выхода: git add (когда конфликт разрешён) или git merge --abort (когда нужно отменить всю попытку слияния).
Важно понять границу этого состояния: MERGING существует только между запуском git merge и его завершением. Как только слияние завершено — либо через add + commit, либо через --abort — MERGING исчезает, и возможность для --abort вместе с ним.
git merge --abort: команда, которая отматывает всё назад
Находясь в состоянии MERGING, достаточно одной команды, чтобы отменить всю попытку слияния:
git merge --abort
Для папки prepared/merge_conflict, где перед git merge feature рабочая папка и индекс были чистыми, эффект предсказуем и полный: Git убирает конфликтные маркеры из README.md, восстанавливает индекс в то состояние, в котором он был до merge, и не создаёт никакого нового коммита.
Для независимого опыта распакуйте весь git_practice.zip в новую отдельную папку через файловый менеджер. Откройте терминал в этой новой папке и перейдите в готовый репозиторий:
cd "git_practice/prepared/merge_conflict"
В нём уже есть .git: повторный git init не нужен. Исходная ветка — main, рабочая папка и индекс чистые; main и feature уже разошлись и изменили одну строку статуса по-разному. Незавершённого merge пока нет. Используйте эту свежую папку, даже если уже разрешали конфликт в другом опыте.
Перед коммитом при повторном слиянии задайте обе локальные настройки автора знакомыми командами. Учебные настройки уже могут быть в архиве; эти команды явно зададут автора для вашего опыта:
git config user.name "Student"
git config user.email "student@example.com"
Проверьте исходный граф и выполните опыт без посторонних правок между merge и abort:
git log --graph --oneline --all
На графе видны расходящиеся main и feature. Команды для прерывания:
# Убедиться в чистом исходном состоянии
git status
# Ожидаемый вывод: nothing to commit, working tree clean
# Запустить слияние — получить конфликт
git merge feature
# Вывод:
# CONFLICT (content): Merge conflict in README.md
# Automatic merge failed; fix conflicts and then commit the result.
# Подтвердить конфликт: unmerged paths и both modified: README.md
git status
# Прервать слияние
git merge --abort
# Проверить восстановление
git status
# Ожидаемый вывод: nothing to commit, working tree clean
Первый и последний git status показывают одно и то же. Открыв README.md после --abort, вы увидите содержимое ветки main без каких-либо маркеров — файл вернулся точно к тому, чем он был до попытки слияния.
Если запустить git merge --abort, когда никакого незавершённого слияния нет, Git ответит коротко:
fatal: There is no merge in progress (MERGE_HEAD missing).
Это важно: команда не молчит, если применять её не по адресу.
Оговорка, которую стоит помнить: гарантия чистого восстановления работает именно для случая, когда рабочая папка и индекс были чистыми до git merge. Если во время конфликта вы успели что-то дополнительно отредактировать, --abort постарается откатить, но эти посторонние правки могут не сохраниться. В этом опыте дополнительных правок между merge и abort нет, поэтому восстановление проверяется относительно чистого исходного состояния.
Когда прерывать слияние, а когда разрешать конфликт
Оказавшись в MERGING с конфликтом в README.md, нужно принять решение. Оба варианта приводят к чистому git status в конце, но они делают разное.
Путь A — git merge --abort подходит, когда слияние нужно отменить полностью:
- Вы ошиблись веткой: хотели слить
bugfix, а запустилиfeature. - Нужно сначала подправить одну из веток, а потом повторить попытку.
- Конфликт слишком сложный и требует предварительного обсуждения с командой.
Результат: репозиторий выглядит так, как будто git merge никогда не запускался.
Путь B — разрешить конфликт и завершить слияние подходит, когда слияние правильное и его нужно довести до конца. После уже выполненного abort маркеров нет и MERGING снято. Для продолжения опыта снова запустите слияние:
git merge feature
git status
Сообщение о конфликте и both modified: README.md подтверждают, что повторный merge опять ждёт решения. Теперь можно завершить его знакомым способом:
- Открыть конфликтующий файл, убрать маркеры, собрать итоговый текст.
git add README.md— пометить файл как разрешённый.git commit -m "Merge feature: объединить правки обеих сторон"— создать merge-коммит.
Проверьте результат:
git status
git log --graph --oneline --all
В истории появляется merge-коммит с двумя родителями, git status показывает «nothing to commit, working tree clean».
Оба пути выводят репозиторий из MERGING в чистое состояние. Критерий выбора простой: если слияние было ошибкой или его нужно подготовить — abort; если слияние правильное и вы готовы решить конфликт сейчас — разрешить и зафиксировать.
git merge --abort и git revert: что общего и в чём принципиальная разница
Оба инструмента позволяют «отменить» что-то в Git, но применяются к принципиально разным ситуациям.
git merge --abort работает с незавершённым процессом. Merge-коммита ещё нет — история не изменилась. Команда прекращает этот процесс и возвращает репозиторий к состоянию до git merge. Никакого следа в истории не остаётся.
git revert <хеш> работает с уже сохранённым обычным коммитом (с одним родителем). Тот коммит никуда не исчезает — он остаётся в истории. Revert добавляет новый коммит, который применяет противоположные изменения. История становится длиннее, а не короче.
Сравнение по ключевым точкам:
- Когда применяется:
--abort— пока в состоянии MERGING;revert— после завершённого коммита. - Есть ли коммит в истории до отмены:
--abort— нет;revert— да, именно на него и ссылаются. - Появляется ли новый коммит:
--abort— нет;revert— да, он и есть результат.
Одна ситуация, которая находится вне этого урока: отмена уже созданного merge-коммита (с двумя родителями) через revert требует указания, с точки зрения какого родителя считать «обратные» изменения. Это отдельная процедура, которую мы здесь не рассматриваем. В сравнении выше revert — всегда обычный коммит с одним родителем.
Задача 1
Учебная папка была чистой. После merge feature конфликт ещё не разрешён; посторонних правок во время merge нет. Нужно полностью отказаться от незавершённого слияния. Что подходит?
Задача 2
Самостоятельная практика:
Откройте папку prepared/merge_conflict из архива git_practice.zip. Начальное состояние: HEAD на main; ветки main и feature разошлись, обе изменили одну строку в README.md; рабочая папка чистая.
Задача в двух частях:
Часть 1 — abort:
- Выполните
git status— убедитесь: «nothing to commit, working tree clean». - Выполните
git merge feature— получите конфликт. - Выполните
git status— зафиксируйте состояние MERGING и пометкуboth modified: README.md. - Выполните
git merge --abort. - Выполните
git status— убедитесь: «nothing to commit, working tree clean». - Откройте
README.mdи убедитесь, что конфликтных маркеров нет.
Часть 2 — resolve:
7. Выполните git merge feature повторно.
8. Откройте README.md, уберите все три маркера (<<<<<<<, =======, >>>>>>>), соберите итоговый текст с правками обеих сторон.
9. Выполните git add README.md.
10. Выполните git commit -m "Merge feature: объединить правки обеих сторон".
11. Выполните git status — должен показать «nothing to commit, working tree clean».
12. Выполните git log --graph --oneline --all — на графе должен появиться merge-коммит с двумя входящими линиями.
Доступный API: git status, git merge feature, git merge --abort, git log --graph --oneline --all, git add, git commit -m.
Письменное объяснение:
Коллега говорит: «Я запустил git merge feature, получил конфликт и не стал его разрешать — просто закрыл терминал и открыл новый. Теперь хочу использовать git revert, чтобы вернуть всё как было, ведь revert отменяет изменения». Объясните: в каком состоянии сейчас находится репозиторий, почему git revert здесь неприменим и какую команду нужно использовать вместо него.
Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.
Контрольный случай:
После конфликтного merge закрыли терминал и открыли новый в той же папке. Других действий не было. Теперь status снова показывает unmerged paths. Какой вывод верен?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
