Объединение веток
В отдельной ветке мы добавили пояснение о начале обучения. Основная линия пока сохранила прежний текст. Теперь представим, что одновременно решили немного изменить зелёный цвет ссылок. Эти задачи затрагивают разные файлы, но обе должны войти в следующую версию каталога.
Объединение веток позволяет получить согласованный результат, сохранив связь с обеими линиями истории. Рассмотрим роль общего предка, отличие обычного слияния от fast-forward и содержимое итогового снимка. Оглавление курса и архив дают сценарий lesson-05; команды не выполнялись, поэтому результаты ещё предстоит получить в лаборатории.
Две задачи после общего состояния
Исходное положение: main указывает на C, ветка feature/course-summary содержит новый коммит D. В Markdown там добавлен абзац «Выберите тему и начните с первого урока.». Рабочее дерево чистое. Если начинаете с этой страницы, сценарий архива показывает, как подготовить обе ветки в новой отдельной папке.
Переключитесь на main и в public/theme.css замените значение --link: #2d9b58 на --link: #24814a. Остальной CSS оставьте прежним. Зафиксируем только это изменение:
git switch main
git diff -- public/theme.css
git add public/theme.css
git diff --cached
git commit -m "Уточнить зелёный цвет ссылок"
git log --oneline --graph --all
Новый коммит основной линии обозначим E. Его родитель — C, как и у D. Это последнее общее состояние, от которого независимо пошли две правки. В рабочей папке main сейчас новое значение цвета, но ещё нет поясняющего абзаца.
D feature/course-summary
/
A — B — C
\
E main
Оба автора мысленно редактировали одну версию каталога, но изменили разные её части. Чтобы объединить результат, нужно рассмотреть изменение каждой стороны относительно C. Простое копирование всей папки feature поверх main потеряло бы новый цвет, потому что снимок feature ещё содержит старый CSS.
Слияние относительно предка
Оставаясь на чистой main, выполните:
git status --short
git merge feature/course-summary -m "Объединить пояснение каталога и оформление"
git status --short
git show --no-patch --format='%h %p %s' HEAD
При заданных правках ожидается успешное автоматическое объединение. Новый снимок содержит и абзац из Markdown, и цвет основной линии. Merge-коммит обозначим M: у него два родителя, E и D. Первый отражает ветку, на которой начали слияние, второй — присоединённую вершину.
D ──────┐
/ │
A — B — C — E — M main
В форматированном выводе show после короткого идентификатора коммита должны быть два идентификатора родителей. Их действительные значения зависят от вашей лаборатории. Схематические буквы нельзя использовать как SHA. Само число родителей здесь помогает связать сохранённый результат с двумя линиями работы.
Почему конфликт не ожидается? Относительно общего предка feature меняет Markdown, а main меняет CSS. Git может перенести обе независимые правки в итоговое дерево. Это не означает, что любые изменения разных файлов всегда совместимы по смыслу: один файл может поменять название класса, а другой продолжать использовать старое имя.
Наш пример специально разделяет роли. Новый абзац не упоминает цвет и не вводит новый класс. Цвет не требует изменения Markdown. Поэтому после автоматического слияния можно прочитать оба файла и проверить, что намерения обеих задач представлены без дополнительных условий. Исполнение сайта для понимания такого текстового результата пока не нужно.
Документация git-merge объясняет объединение историй. Практический вывод для каталога: слияние работает с отношением изменений к предку, а не выбирает одну папку как безусловно правильную. Человек по-прежнему отвечает за смысл итоговой версии.
Когда получается fast-forward
Вернёмся мысленно к концу предыдущего урока, до создания E. Тогда main указывала на C, а D был его прямым потомком. Если объединять именно это состояние, можно просто передвинуть main на D. Такой шаг называют fast-forward: отдельный merge-коммит для объединения двух расходящихся линий не нужен, поскольку расхождения ещё не было.
до: C main → D feature
после: C — D main, feature
Эта схема иллюстрирует расположение ссылок, а не дополнительный опыт внутри текущей лаборатории. После появления E основная линия уже не является предком D, поэтому простое передвижение не объединит обе правки. Не возвращайте репозиторий назад ради демонстрации: независимый вариант можно изучить позже в новой папке.
Иногда процесс команды требует явно сохранять границу feature через merge-коммит даже при возможности fast-forward. У Git есть параметры для разных стратегий записи истории. В первом курсе важнее увидеть причину различия графов, чем выбирать параметр по привычке. Наш основной сценарий уже расходится и поэтому показывает естественный коммит с двумя родителями.
Чтение итогового состояния
Откройте content/courses.md: там должны быть исходные ссылки и поясняющий абзац. Откройте public/theme.css: значение --link должно быть #24814a. Затем посмотрите историю и статус. Чистый статус подтверждает согласованность рабочего дерева с индексом и последним снимком, но не оценивает качество текста.
Не удаляйте feature в середине разбора: её имя удобно для чтения графа. Сама ветка всё ещё указывает на D, тогда как main указывает на M. Слияние не передвигает присоединённую ссылку автоматически. Продолжение коммитов на feature после этого создаст её дальнейшую линию, которую придётся отдельно рассматривать.
Если вместо ожидаемого результата возник конфликт, остановитесь и прочитайте статус. Возможно, исходная версия отличалась от сценария или вы изменили другие строки. Не заменяйте целиком файлы одним из вариантов, чтобы скрыть проблему. До начала операции дерево должно быть чистым; при отказе от начатого слияния в таком случае доступна команда git merge --abort.
Результат полезно оценить и с точки зрения человека, который увидит каталог впервые. В описании говорится, что нужно выбрать тему, а ссылка визуально использует новый цвет. Это две части одной страницы, но причины их изменения разные. Merge сохраняет возможность отдельно найти редакционный коммит и коммит оформления. Такое разделение помогает обсуждать ошибку: если формулировка не подходит, необязательно возвращать прежний цвет вместе с ней.
При чтении merge-коммита также важно выбрать, с каким родителем сравнивается содержимое. Относительно прежней основной линии новыми будут абзацы feature, относительно feature — правка оформления. Один итоговый снимок допускает разные сравнения, поскольку соединяет две линии. Это объясняет, почему краткий просмотр истории иногда выглядит иначе, чем ожидал автор: отображение diff зависит от выбранной точки отсчёта. Сначала назовите вопрос — что пришло из feature или что стало итогом объединения — и затем выбирайте сравнение.
Наконец, общая история не заменяет самостоятельного согласования выпуска. Даже когда обе задачи находятся на main, посетители не обязаны видеть их сразу. Наша лаборатория вообще не подключена к серверу. При реальной разработке состояние основной ветки становится исходным материалом для сборки и проверки; когда его публиковать, определяет процесс проекта. Поэтому здесь результатом является согласованное дерево Git, а не изменение действующего сайта.
Текущий результат — один снимок с двумя согласованными изменениями и сохранённой историей их происхождения. В следующем уроке обе ветки изменят заголовок одной страницы. Тогда разрешение конфликта потребует выбрать текст, который выразит оба намерения.