Конфликт Git в одном компоненте: понять обе правки и сохранить поведение
- 18 августа 2026
Две ветки изменили кнопку покупки. Одна отключает её для отсутствующего товара, другая уточняет подпись. При слиянии Git остановился. Сохраним оба требования и проверим кнопку: исчезновение маркеров конфликта ещё не гарантирует правильное поведение.
Создадим конфликт в отдельном учебном репозитории. Он не подключён к удалённому серверу и не использует ваш рабочий проект. В конце обе ветки останутся в истории, а компонент получит общую реализацию.
Если нужны основные команды, держите рядом шпаргалку по Git. Здесь будем разбирать содержимое правок. Рекомендуем сначала сформулировать требования каждой стороны словами, а затем собирать код: кнопки «принять текущее» и «принять входящее» могут скрыть потерянную функциональность.
Подготовьте учебный репозиторий
Понадобятся установленный Git с командами switch и init -b, редактор, браузер и Node.js для небольшого сервера файлов. Достаточно понимать функцию JavaScript, свойства объекта и работу кнопки в HTML.
В папке, где вы храните упражнения, выполните:
mkdir git-conflict-lab
cd git-conflict-lab
git init -b main
git config user.name 'Учебный автор'
git config user.email 'learner@example.invalid'Если папка уже существует, выберите другое имя и начните в новой пустой папке. Настройки автора здесь локальные: команды без --global относятся к учебному репозиторию. Удалённый адрес для упражнения не нужен.
Создайте файл button.js:
export function updateButton(button, product) {
button.textContent = 'В корзину';
}Второй файл, index.html, даст способ проверить компонент на двух вымышленных товарах:
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Кнопка каталога</title>
</head>
<body>
<main>
<h1>Учебный каталог</h1>
<label for="product">Выберите товар</label>
<select id="product">
<option value="0">Лампа, есть в наличии</option>
<option value="1">Плед, нет в наличии</option>
</select>
<button id="buy" type="button"></button>
</main>
<script type="module">
import { updateButton } from './button.js';
const products = [
{ name: 'лампу', available: true },
{ name: 'плед', available: false },
];
const select = document.querySelector('#product');
const button = document.querySelector('#buy');
function render() {
updateButton(button, products[Number(select.value)]);
}
select.addEventListener('change', render);
render();
</script>
</body>
</html>Поле name содержит учебную форму названия для подписи кнопки. В реальном каталоге текст составляется с учётом языка интерфейса; автоматическое склонение товаров не входит в задачу.
Сохраните исходное состояние:
git add index.html button.js
git commit -m 'Create training catalog button'
git status --shortПоследняя команда должна завершиться без списка изменений. У нас есть общий исходный коммит, от которого начнётся работа обеих веток.
Добавьте ограничение наличия в первой ветке
Создайте ветку:
git switch -c feature/stockЗамените содержимое button.js:
export function updateButton(button, product) {
button.disabled = !product.available;
button.textContent = product.available ? 'В корзину' : 'Нет в наличии';
}Теперь доступность товара управляет и состоянием кнопки, и её подписью. Одного текста «Нет в наличии» недостаточно: нативное свойство disabled не позволяет активировать кнопку обычным нажатием.
Запишите эту правку и вернитесь к исходной ветке:
git add button.js
git commit -m 'Disable purchase for unavailable products'
git switch mainВ рабочем файле снова будет первоначальное 'В корзину'. Это ожидаемо: переключение ветки восстановило её версию файла. Коммит с проверкой наличия сохранён в feature/stock.
Измените подпись во второй ветке
Из main создайте отдельную ветку. Если создать её из feature/stock, история получится другой и нужный конфликт не воспроизведётся.
git switch -c feature/labelВо второй ветке button.js должен выглядеть так:
export function updateButton(button, product) {
button.textContent = 'Добавить ' + product.name + ' в корзину';
}Эта правка добавляет название товара. Проверки наличия здесь пока нет, потому что ветки развивались независимо.
git add button.js
git commit -m 'Name product in purchase button'
git -c merge.conflictStyle=merge merge feature/stockСлияние выполняется в текущую ветку feature/label. Параметр -c задаёт обычный вид маркеров для этой команды; постоянную настройку Git он не меняет. Ожидаем сообщение о конфликте в button.js и остановку до создания коммита слияния. Коммита слияния пока нет.
Git умеет объединять многие изменения автоматически, но разные изменения одной области файла требуют решения. Порядок действий и состояние незавершённого слияния описаны в главе Pro Git о конфликтах.
Прочитайте обе стороны конфликта
Откройте button.js. Конфликтный участок будет похож на этот:
export function updateButton(button, product) {
<<<<<<< HEAD
button.textContent = 'Добавить ' + product.name + ' в корзину';
=======
button.disabled = !product.available;
button.textContent = product.available ? 'В корзину' : 'Нет в наличии';
>>>>>>> feature/stock
}В этом обычном слиянии HEAD — текущая ветка feature/label. Ниже разделителя — правка из feature/stock. Имена «текущее» и «входящее» описывают направление операции, поэтому по ним нельзя судить о том, какая версия содержательно правильнее.
Зафиксируйте два требования: доступный товар получает подпись с названием; отсутствующий товар получает отключённую кнопку с сообщением о наличии. Эти требования совместимы. Нам предстоит написать вариант, которого ещё нет ни в одной ветке.
Когда контекста недостаточно, посмотрите сохранённые версии без изменения файлов:
git show HEAD:button.js
git show feature/stock:button.js
git log --oneline --all -- button.js
git statusКоманда git show выводит объект из истории; синтаксис с путём описан в её справочнике. В рабочем проекте к истории добавьте описание задачи и обсуждение с автором правки. По одному фрагменту функция может выглядеть понятной, но её ограничения часто находятся в вызывающем коде.
Не выбирайте одну сторону целиком, пока обе задачи нужны. Если оставить версию подписи, отсутствующий плед останется доступным для покупки. Если оставить версию наличия, лампа потеряет своё название в кнопке. Оба результата выглядят правдоподобно.
Соберите общий компонент и проверьте поведение
Замените весь конфликтный файл следующим содержимым, удалив все маркеры:
export function updateButton(button, product) {
button.disabled = !product.available;
if (!product.available) {
button.textContent = 'Нет в наличии';
return;
}
button.textContent = 'Добавить ' + product.name + ' в корзину';
}При каждом вызове свойство disabled вычисляется заново. Это нужно при переключении с отсутствующего пледа обратно на лампу: прежнее true не должно остаться на кнопке. Затем ранний return отделяет недоступный товар, а оставшаяся ветка сохраняет уточнённую подпись.
Проверьте текстовые ошибки:
git diff --check
git diff -- button.jsОпция --check помогает найти маркеры конфликта и ошибки пробелов; её назначение описано в документации git diff. Поведение функции она не проверяет.
Для браузера нужен HTTP-сервер, поскольку страница импортирует модуль. Создайте рядом временный preview.mjs:
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
const pages = {
'/': [await readFile(new URL('./index.html', import.meta.url)), 'text/html'],
'/button.js': [await readFile(new URL('./button.js', import.meta.url)), 'text/javascript'],
};
createServer((req, res) => {
const page = pages[req.url];
res.writeHead(page ? 200 : 404, {
'Content-Type': page ? page[1] + '; charset=utf-8' : 'text/plain',
});
res.end(page ? page[0] : 'Not found');
}).listen(3000, '127.0.0.1');Запустите node preview.mjs и откройте http://127.0.0.1:3000. После изменения файла сервер нужно перезапустить: этот учебный вариант читает файлы при запуске.
| Действие | Ожидаемый результат |
|---|---|
| Выбрать лампу | «Добавить лампу в корзину», кнопка доступна |
| Переключить на плед | «Нет в наличии», кнопка отключена |
| Вернуться к лампе | Уточнённая подпись и доступная кнопка восстановлены |
| Просмотреть button.js | Маркеров конфликта нет, обе задачи выражены кодом |
Завершите слияние после проверки
Добавьте только исправленный компонент в индекс и посмотрите результат:
git add button.js
git diff --cached -- button.js
git status
git commit -m 'Merge stock restriction with descriptive button label'После git add конфликт исчезает из списка неслитых путей: Git считает файл разрешённым. Коммит завершает слияние; это порядок из справочника git merge. Команда git add . здесь не нужна: она могла бы прихватить служебный сервер предпросмотра.
Файл preview.mjs останется неотслеживаемым учебным инструментом. Это не незавершённый конфликт. Проверьте именно список неслитых путей: git diff --name-only --diff-filter=U должен ничего не вывести.
Самостоятельная задача: добавьте товар с длинным названием и смените его наличие несколько раз. Затем объясните, почему строка с disabled находится до проверки с ранним выходом. Если можете показать потерю любой из двух функций при выборе одной стороны, содержимое конфликта вы разобрали.
Закрепите работу с изменениями интерфейса
В рабочем проекте используйте тот же порядок: прочитайте требования обеих правок, соберите совместимое поведение и выполните проверки проекта. Синтаксически чистый diff нужен для коммита; проверка интерфейса показывает, сохранились ли задачи команды.
Если основы JavaScript и DOM уже знакомы, сверяйте следующий шаг с программой курса «JavaScript. Профессиональная разработка веб-интерфейсов». Перед выбором программы проверьте входные требования, а в учебном репозитории оставьте коммит слияния и таблицу наблюдений за кнопкой.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.