Урок курса

Отказаться от незавершённого слияния

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:

  1. Выполните git status — убедитесь: «nothing to commit, working tree clean».
  2. Выполните git merge feature — получите конфликт.
  3. Выполните git status — зафиксируйте состояние MERGING и пометку both modified: README.md.
  4. Выполните git merge --abort.
  5. Выполните git status — убедитесь: «nothing to commit, working tree clean».
  6. Откройте 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. Какой вывод верен?

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

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

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

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