Вернуться к главной странице, списку всех тем
3. Управление версиями проектов, автоматизация при внесении изменений
Что ты узнаешь: как хранить историю изменений кода, работать над проектом вместе с другими, не затирая чужую работу, и запускать автоматические проверки при каждом изменении
Что нужно знать заранее: темы 1 и 2 — терминал, файлы и права, SSH
Сколько времени займёт: 2 часа теории + 6 часов практики
Твой шаг в сквозном проекте: положишь «Заметки» в git-репозиторий на GitHub и настроишь пайплайн, который сам проверяет код при каждом изменении
Представь, что вы с друзьями вместе пишете книгу. Версий текста много, каждый постоянно что-то добавляет и удаляет. Чтобы не запутаться, вы используете Git. Его цель — следить, кто и что изменил, и позволять вернуться к старым версиям, если что-то пошло не так
Для DevOps-инженера Git — это ещё и нечто большее. Дальше в курсе всё, что мы делаем, будет описано текстовыми файлами: конфиг nginx, Dockerfile, манифесты Kubernetes, плейбуки Ansible. Все они лежат в git. Отсюда следует правило, к которому мы придём в теме 9: состояние инфраструктуры — это то, что записано в репозитории, а не то, что кто-то когда-то руками поправил на сервере
Основные задачи Git
- Отслеживание изменений. Каждый раз, когда ты фиксируешь работу, Git запоминает состояние проекта — создаёт «коммит». Можно вернуться к любой предыдущей версии
- История изменений. Git хранит всю историю с комментариями, зачем правка была нужна. Через год именно комментарий объяснит, почему в конфиге стоит странное значение
- Совместная работа. Несколько человек работают над проектом одновременно и объединяют изменения, разрешая расхождения
- Управление ветками. Ветка — отдельная линия разработки. Одна команда делает новую функцию в своей ветке, другая чинит ошибку в основной. Когда всё готово, ветки объединяют
Основные понятия Git
- Репозиторий — каталог с файлами проекта и историей их изменений. История и настройки лежат в скрытой директории
.git - Коммит — зафиксированное состояние проекта с описанием, что было сделано, и уникальным идентификатором (хешем)
- Ветка (branch) — отдельная линия разработки, подвижный указатель на коммит
- Слияние (merge) — объединение изменений из одной ветки в другую
- Конфликт — ситуация, когда двое изменили одну и ту же строку и Git не может решить сам, чью версию оставить
- Клонирование (clone) — создание локальной копии удалённого репозитория
- Push / Pull — отправка изменений на удалённый сервер и получение их оттуда
- Tag — неподвижная метка на конкретном коммите, обычно для обозначения версии релиза
- Remote — ссылка на удалённый репозиторий; по умолчанию называется
origin
Три состояния файла — то, что нужно понять в первую очередь
Это единственная действительно новая идея в Git, всё остальное — команды поверх неё. Файл в репозитории находится в одном из состояний:
рабочий каталог ──git add──▶ индекс (staging) ──git commit──▶ история (.git)
ты правишь «пойдёт в коммит» «зафиксировано навсегда»
- Рабочий каталог — файлы, которые ты видишь и правишь
- Индекс (staging area) — черновик следующего коммита. Сюда попадает то, что ты выбрал командой
git add - История — коммиты, которые уже нельзя случайно потерять
Двухшаговость (add, потом commit) нужна, чтобы из десяти правок собрать один осмысленный коммит, а не свалить всё в кучу. Посмотреть, что в каком состоянии, — git status. Эту команду опытный инженер запускает десятки раз в день
Куда указывает HEAD
HEAD — указатель на то место в истории, где ты сейчас находишься. Обычно он указывает на ветку, а ветка — на последний её коммит. git switch другая-ветка двигает HEAD, и файлы в рабочем каталоге меняются на соответствующие. Если HEAD указывает прямо на коммит, а не на ветку, это состояние называется detached HEAD — коммиты в нём делать можно, но они потеряются, если не создать ветку
GitHub и GitLab
GitHub — онлайн-платформа для хранения git-репозиториев и совместной работы. Огромный склад, где программисты хранят свои репозитории, делятся ими и работают вместе
Этот курс размещён на GitHub в виде репозитория — ты читаешь его страницы, которые собираются прямо из markdown-файлов
GitLab — похожая платформа с ключевым отличием: её можно установить на собственный сервер. Крупные компании часто выбирают именно её, потому что хотят хранить код внутри своего контура. Понятия почти совпадают, но названия разные:
| GitHub | GitLab | Что это |
|---|---|---|
| Pull Request (PR) | Merge Request (MR) | Запрос на слияние ветки с обсуждением |
| GitHub Actions | GitLab CI | Система автоматизации |
.github/workflows/*.yml |
.gitlab-ci.yml |
Файл с описанием пайплайна |
| Runner | Runner | Машина, которая выполняет шаги пайплайна |
Как работают командой: ветки и Pull Request
Правило почти любой команды: в основную ветку (main) не коммитят напрямую. Вместо этого:
- Создаёшь ветку от
main:git switch -c feature/add-healthcheck - Делаешь в ней коммиты
- Отправляешь ветку на сервер:
git push -u origin feature/add-healthcheck - Открываешь Pull Request — просишь влить свою ветку в
main - Коллеги читают изменения, оставляют замечания, автоматика прогоняет проверки
- Когда всё зелено и одобрено — ветку вливают, а потом удаляют
Зачем эта церемония: в main попадает только просмотренный и проверенный код, а история изменений становится документацией — по каждому PR видно, что меняли и почему
Имена веток обычно осмысленные: feature/имя для новой функции, fix/имя для исправления, chore/имя для рутины
Как писать сообщения коммитов
Плохо: фиксы, ., работает, ещё раз. Через месяц никто, включая тебя, не поймёт, что произошло
Хорошо: первая строка — краткая суть в повелительном наклонении, до 72 символов; при необходимости пустая строка и подробности с ответом на вопрос «почему»:
Добавить healthcheck-эндпоинт в «Заметки»
Kubernetes из темы 5 умеет перезапускать зависшие поды,
но для этого ему нужен URL, который отвечает быстро и без
обращения к базе. /healthz именно такой.
Распространённое соглашение — Conventional Commits: feat:, fix:, docs:, chore:, refactor:. По таким префиксам автоматика умеет сама собирать список изменений релиза
.gitignore: что нельзя класть в репозиторий
Файл .gitignore перечисляет то, что Git должен игнорировать:
*.log # логи
__pycache__/ # временные файлы Python
.env # файл с паролями и токенами
node_modules/ # скачанные зависимости
*.key # приватные ключи
Главное правило: в репозиторий не кладут секреты. Пароль, попавший в коммит, остаётся в истории навсегда, даже если ты удалишь его следующим коммитом — история хранит всё. Если это случилось, пароль считается скомпрометированным и его нужно менять. Как хранить секреты правильно, разберём в теме 9 на примере Vault
CI/CD: автоматизация
Представь, что вы строите дом на колёсах:
- CI (Continuous Integration), интеграция — каждый член команды делает свою часть: кто-то возводит стены, кто-то ставит окна, кто-то тянет проводку. В конце дня нужно проверить, что все части стыкуются друг с другом
- CI, проверка — работают ли окна, нет ли трещин, исправен ли свет. Это сборка и тесты
- CD (Continuous Delivery/Deployment), доставка — готовый дом передают владельцам. Это выкладка на сервер
CI/CD делает этот процесс автоматическим: не нужно ждать, пока кто-то вручную выполнит каждый шаг. Автоматизированная последовательность шагов называется пайплайн (pipeline)
Пайплайн запускается по событию в репозитории: коммит, коммит в конкретную ветку, открытие Pull Request, слияние, выпуск тега, наступление времени по расписанию
Типичные шаги пайплайна:
- Линтинг — проверка, что код и конфиги написаны по правилам и без синтаксических ошибок. Самый дешёвый шаг, поэтому он всегда идёт первым
- Тесты — автоматическая проверка, что программа делает то, что должна
- Сборка — превращение исходного кода в то, что можно запустить: исполняемый файл или образ контейнера. Как рецепт превращается в блюдо
- Проверка безопасности — поиск известных уязвимостей в зависимостях и случайно закоммиченных секретов
- Публикация — отправка результата сборки (артефакта) в хранилище
- Развёртывание — выкладка на сервер
- Уведомление — сообщение команде в Telegram или почту, особенно если что-то упало
Порядок не случаен: шаги выстраивают от быстрых и дешёвых к медленным и дорогим, чтобы опечатка отваливалась за 10 секунд, а не после пятнадцатиминутной сборки. Этот принцип называется fail fast
Артефакт — результат сборки: архив, пакет, образ контейнера, отчёт о тестах. В теме 4 нашим артефактом станет docker-образ
Runner — машина (обычно контейнер), на которой выполняются шаги пайплайна. У GitHub есть бесплатные runner’ы в облаке; компании часто поднимают свои
Теоретические вопросы
-
Что такое Git и зачем он нужен? Ответ: Система контроля версий: отслеживает изменения файлов и позволяет нескольким людям работать над проектом одновременно, не мешая друг другу. Даёт возможность вернуться к любой прошлой версии
-
Чем репозиторий отличается от обычной папки? Ответ: В репозитории есть скрытый каталог
.gitс полной историей, настройками и метаданными. Удали его — останется обычная папка с файлами, но без истории -
Что такое staging area и зачем нужен отдельный шаг
git add? Ответ: Промежуточная область, черновик следующего коммита. Позволяет собрать в один коммит только связанные изменения, а не всё подряд. Аналогия:add— положить товары в корзину,commit— оплатить на кассе -
Что хранится в коммите? Ответ: Снимок состояния файлов, автор, дата, сообщение, ссылка на родительский коммит и уникальный хеш. По хешу коммит можно найти всегда
-
Зачем нужны ветки? Ответ: Чтобы вести параллельную разработку, не трогая стабильный код. Шоссе с основным движением (
main) и съездами для новых функций; готовый съезд подключают обратно -
Чем ветка отличается от тега? Ответ: Ветка — подвижный указатель, он переезжает на каждый новый коммит. Тег — неподвижная метка на конкретном коммите, обычно версия релиза (
v1.2.0) -
Что такое
HEAD? Ответ: Указатель на текущую позицию в истории — обычно на ветку, в которой ты находишься. Именно отHEADотсчитываютсяHEAD~1(предыдущий коммит) и подобные обозначения -
Что такое merge conflict и почему он возникает? Ответ: Git не может автоматически объединить изменения, потому что одна и та же строка была изменена по-разному в двух ветках. Решение выбирает человек: Git размечает спорное место маркерами
<<<<<<<,=======,>>>>>>> -
Чем
mergeотличается отrebase? Ответ:mergeобъединяет ветки, сохраняя обе истории и создавая коммит слияния.rebaseпереносит коммиты поверх другой ветки, делая историю линейной, но переписывая её. Правило: не делатьrebaseветок, которые уже отправлены и используются другими -
Что такое fast-forward merge? Ответ: Слияние без создания отдельного коммита: если в основной ветке не было новых коммитов, её указатель просто передвигается вперёд
-
Чем
git resetотличается отgit revert? Ответ:resetдвигает указатель ветки назад, убирая коммиты из истории — опасен для веток, которые уже отправлены.revertсоздаёт новый коммит, отменяющий изменения, история сохраняется. В общих ветках используют толькоrevert -
Чем
git fetchотличается отgit pull? Ответ:fetchскачивает изменения с сервера, но не трогает твою рабочую ветку.pull=fetch+ автоматическое слияние.fetch— «принести письма»,pull— «принести и сразу вклеить в дневник» -
Чем Git отличается от GitHub? Ответ: Git — программа на твоём компьютере, работающая с версиями. GitHub — сайт, где репозитории хранят и обсуждают. Git — фотоаппарат, GitHub — фотогалерея в интернете. Git прекрасно работает и без GitHub
-
Чем
cloneотличается отfork? Ответ:clone— копия репозитория на твоём компьютере.fork— копия чужого репозитория в твоём аккаунте на GitHub, нужна, чтобы предложить изменения в проект, куда у тебя нет прав на запись -
Что такое Pull Request и зачем он нужен? Ответ: Запрос на слияние ветки с возможностью обсуждения. Даёт ревью коллег и автоматические проверки до попадания кода в
main. Черновик статьи, который отправляют редактору перед публикацией -
Для чего нужен
.gitignore? Ответ: Перечислить, что Git не должен отслеживать: логи, кеши, скачанные зависимости, файлы с паролями. Список «не класть в чемодан» -
Что делать, если пароль случайно попал в коммит? Ответ: Считать пароль скомпрометированным и немедленно сменить его. Удаление следующим коммитом не помогает — старый коммит остаётся в истории и доступен любому, у кого есть копия репозитория
-
Что такое CI и CD? Ответ: CI — автоматическая проверка и сборка кода при каждом изменении. CD — автоматическая доставка результата на сервер. CI — проверка деталей на заводе, CD — отправка изделия клиенту
-
Что такое pipeline и какие события его запускают? Ответ: Цепочка автоматических шагов. Запускается коммитом, открытием или обновлением Pull Request, слиянием, выпуском тега, расписанием либо вручную
-
Почему линтинг ставят раньше сборки? Ответ: Принцип fail fast: шаги идут от дешёвых к дорогим. Опечатку в YAML нет смысла искать после пятнадцатиминутной сборки, если линтер найдёт её за десять секунд
-
Что такое артефакт? Ответ: Результат сборки: архив, пакет, образ контейнера, отчёт о тестах. Именно артефакт, а не исходный код, потом уезжает на сервер
-
Что такое runner? Ответ: Машина или контейнер, где выполняются шаги пайплайна. Бывают облачные (предоставляет платформа) и собственные, поднятые внутри компании — их выбирают, когда пайплайну нужен доступ во внутреннюю сеть
-
Как выглядит хороший пайплайн? Ответ: Линтинг → тесты → сборка → проверка безопасности → публикация артефакта → развёртывание → уведомление команды. Быстрый на старте, воспроизводимый, с понятным сообщением об ошибке
Вопросы с собеседований
Отдельный блок про GitLab CI — в российских компаниях спрашивают о нём чаще, чем о GitHub Actions
-
Какие основные поля есть у задачи в GitLab CI? Ответ:
stage(к какому этапу относится),image(в каком образе выполнять),script(команды),before_scriptиafter_script,rulesилиonly/except(когда запускать),artifacts(что сохранить после выполнения),cache(что переиспользовать между запусками),needs(от каких задач зависит),tags(на каком runner выполнять),when(manual— запуск по кнопке) -
Как в CI хранить пароли и токены? Ответ: В переменных CI/CD в настройках проекта, помеченных как
masked(скрывать в логах) иprotected(доступны только защищённым веткам). В репозитории их держать нельзя. Более зрелый вариант — брать секреты из Vault прямо в момент выполнения задачи, об этом тема 9 -
Как сделать, чтобы несколько задач выполнялись одновременно? Ответ: Задачи одного этапа (
stage) по умолчанию идут параллельно — достаточно назначить им один этап. Для более гибкой схемы используютneeds: задача стартует сразу после указанных зависимостей, не дожидаясь всего этапа. Ещё естьparallel: N— запустить одну задачу в нескольких экземплярах, например чтобы разложить тесты -
Что такое артефакты и где они хранятся? Ответ: Файлы, которые задача сохраняет после выполнения: собранный пакет, отчёт о тестах, покрытие кода. Хранятся на сервере GitLab и доступны для скачивания и передачи в следующие задачи. У них задают срок жизни
expire_in, иначе они быстро съедают место -
Что такое окружения stage, preprod и prod? Ответ: Отдельные копии системы под разные цели.
stage(тестовое) — куда попадает каждое изменение для проверки.preprod— максимально похоже на боевое, для финальной проверки и нагрузочных тестов.prod— то, чем пользуются люди. Правило: изменение проходит их по очереди, а выкладка вprodобычно требует ручного подтверждения -
Как склонировать репозиторий по SSH? Ответ:
git clone git@gitlab.com:группа/проект.git, предварительно добавив открытый ключ в настройках профиля. Отличие от HTTPS: не нужно вводить логин и токен при каждой операции. Проверить доступ —ssh -T git@gitlab.com -
Чем GitLab CI отличается от GitHub Actions по устройству? Ответ: В GitLab весь пайплайн описан одним файлом
.gitlab-ci.ymlи построен вокруг этапов. В GitHub Actions несколько файлов в.github/workflows/, а единица переиспользования — готовое действие (action) из общего каталога. Идеи одинаковые, различаются словарь и формат
Git глубже
-
Я закоммитил не в ту ветку. Как перенести коммит? Ответ:
git cherry-pick хеш— применить конкретный коммит в текущую ветку. Дальше убрать его из исходной: если ветка ещё никуда не отправлена, годитсяgit reset --hard HEAD~1; если отправлена и её используют другие — толькоgit revert -
Что делать с незакоммиченными правками, если срочно нужно переключиться на другую ветку? Ответ:
git stashубирает их «в карман» и возвращает чистое дерево,git stash popдостаёт обратно. Список отложенного —git stash list. Это не хранилище: отложенное легко забыть и потерять, поэтому для чего-то ценного лучше сделать временный коммит в отдельной ветке -
Я сделал
reset --hardи потерял коммиты. Их можно вернуть? Ответ: Почти всегда да.git reflogхранит историю перемещенийHEADза последние недели, включая то, что «удалено». Находишь нужный хеш и восстанавливаешься:git reset --hard хешилиgit branch спасение хеш. Знание про reflog отличает того, кто пережил свою первую потерю данных, от того, кому это ещё предстоит -
Как найти коммит, в котором сломалась работающая функция? Ответ:
git bisect— двоичный поиск по истории. Указываешь заведомо рабочий и заведомо сломанный коммиты, git предлагает середину, ты проверяешь и говоришьgoodилиbad. За несколько шагов находится виновник даже среди тысячи коммитов. Процесс можно автоматизировать:git bisect run ./тест.sh -
Что такое squash и когда его применяют? Ответ: Схлопывание нескольких коммитов в один. Применяют при слиянии ветки, где было десять коммитов вида «фикс», «ещё фикс», «точно фикс»: в основную ветку попадает один осмысленный коммит. Обратная сторона — теряется детальная история работы, поэтому в некоторых командах предпочитают сохранять всё
-
Чем аннотированный тег отличается от обычного? Ответ: Обычный (
git tag v1.0) — просто указатель на коммит. Аннотированный (git tag -a v1.0 -m "описание") — полноценный объект с автором, датой, сообщением и возможностью подписи. Для релизов используют аннотированные: по ним видно, кто и когда выпустил версию -
Что такое семантическое версионирование? Ответ: Формат
МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ, например2.4.1. Патч меняют при исправлениях, минорную — при новых возможностях с сохранением совместимости, мажорную — при ломающих изменениях. Это договорённость, позволяющая понять по номеру, безопасно ли обновляться. На неё опирается автоматика: в теме 9 Flux отбирает образы правилом>=1.0.0 -
Из каких объектов состоит git внутри? Ответ: Четыре типа: blob (содержимое файла), tree (каталог: список имён со ссылками на blob и другие tree), commit (ссылка на tree, автор, сообщение, родитель) и tag. Всё адресуется хешем содержимого, поэтому одинаковые файлы хранятся один раз, а изменить историю незаметно невозможно — поменяется хеш и всё, что за ним следует
-
Что такое git hooks? Ответ: Скрипты, запускаемые на события:
pre-commit(перед коммитом — прогнать линтер, поискать случайно вписанные пароли),commit-msg(проверить формат сообщения),pre-push. Лежат в.git/hooksи потому не попадают в репозиторий — чтобы раздать их команде, используют инструменты вроде pre-commit. Хуки не заменяют проверки в CI: локально их легко обойти флагом--no-verify -
Чем trunk-based разработка отличается от Git Flow? Ответ: Git Flow — долгоживущие ветки (
develop,release,hotfix) и редкие крупные релизы. Trunk-based — короткие ветки на день-два, всё быстро вливается в основную, а незаконченные функции прячут за флагами (тема 9). Второй подход лучше сочетается с непрерывной доставкой: чем дольше живёт ветка, тем больнее слияние -
Что такое монорепозиторий и в чём его компромисс? Ответ: Все проекты компании в одном репозитории. Плюсы: общий код переиспользуется без публикации пакетов, изменение через несколько сервисов делается одним коммитом, единые правила. Минусы: репозиторий разрастается, нужны инструменты сборки, понимающие, что именно изменилось, и права доступа настраиваются сложнее
Практическая часть
Задание 0. Настроить Git
Цель: представиться Git, иначе коммиты будут безымянными
Шаги:
git config --global user.name "Твоё Имя"
git config --global user.email "твоя@почта"
git config --global init.defaultBranch main # называть первую ветку main, а не master
git config --global core.editor nano # чем открывать сообщения коммитов
git config --list # проверить, что записалось
Настрой доступ на GitHub по SSH-ключу — это удобнее пароля и понадобится дальше:
ssh-keygen -t ed25519 -C "твоя@почта" # на все вопросы можно нажать Enter
cat ~/.ssh/id_ed25519.pub # это ОТКРЫТЫЙ ключ, его копируем
Скопированное содержимое добавь на GitHub: Settings → SSH and GPG keys → New SSH key. Проверь:
ssh -T git@github.com # ожидаем: Hi <логин>! You've successfully authenticated
Типичные ошибки:
- Скопировать файл без
.pub— это закрытый ключ, его нельзя никуда отправлять. Нужен именноid_ed25519.pub Permission denied (publickey)— ключ не добавлен на GitHub или добавлен не тот файл
Задание 1. Репозиторий и первые коммиты
Цель: понять связку «рабочий каталог → индекс → история»
Шаги:
mkdir my-first-project && cd my-first-project
git init
git status # смотри на вывод после каждого шага ниже
printf 'Alice\nBob\nCharlie\n' > team.txt
git status # team.txt в untracked files
git add team.txt
git status # теперь в changes to be committed
git commit -m "Добавить список участников команды"
echo "# Мой первый проект" > project-info.md
git add project-info.md
git commit -m "Добавить описание проекта"
git log --oneline --graph # история в компактном виде
git show HEAD # что именно изменилось в последнем коммите
Ожидаемый вывод: git log --oneline показывает два коммита с короткими хешами
Типичные ошибки:
echo "Alice\nBob"без флага-eзапишет буквальный текстAlice\nBobодной строкой —echoв bash не разбирает\nпо умолчанию. Надёжнееprintf, как в примере вышеPlease tell me who you are— не выполнено задание 0- Сделал
git add, потом ещё правку, потомgit commit— в коммит попадёт только то, что было на моментadd. Проверяйgit statusперед фиксацией
Задание 2. Ветки
Цель: освоить изоляцию работы и слияние
Шаги:
git switch -c feature/add-readme # создать ветку и сразу перейти в неё
# (старый эквивалент: git checkout -b)
printf '# Мой первый проект\nУчебный проект курса DevOps.\n' > README.md
git add README.md
git commit -m "Добавить README"
git switch main
ls # README.md здесь нет — он остался в другой ветке
git log --oneline --all --graph # видно расхождение веток
git merge feature/add-readme # влить
ls # теперь README.md на месте
git branch -d feature/add-readme # удалить отработавшую ветку
Ожидаемый вывод: после merge файл появляется в main; git log --graph показывает, как ветка вернулась в основную линию
Типичные ошибки:
- Переключиться на другую ветку с незакоммиченными правками — Git не пустит. Либо закоммить, либо временно убери изменения:
git stash, а потом верниgit stash pop git branch -dотказывается удалять неслитую ветку — это защита. Осознанное удаление —-Dзаглавной буквой
Задание 3. Подключение GitHub
Цель: научиться работать с удалённым репозиторием
Шаги:
- На GitHub создай репозиторий
devops-practiceбез README -
Подключи локальный репозиторий и отправь код:
git remote add origin git@github.com:<твой_логин>/devops-practice.git git remote -v # проверить, что записалось git branch -M main git push -u origin main # -u запоминает связь, дальше хватит git push - Создай на GitHub через веб-интерфейс ветку
update-docs -
Забери её локально и поработай в ней:
git fetch origin git switch update-docs echo "Новая функция: аутентификация" >> project-info.md git commit -am "Описать функцию аутентификации" git push origin update-docs - На GitHub открой Pull Request из
update-docsвmain, посмотри вкладку Files changed и влей его -
Забери результат к себе:
git switch main git pull git log --oneline
Типичные ошибки:
Updates were rejectedприpush— на сервере есть коммиты, которых нет у тебя. Сначалаgit pull, потомpush. Флаг--forceв общих ветках использовать нельзя: он затрёт чужую работуgit commit -amне добавляет новые файлы, только изменения в уже отслеживаемых. Для новых нужен явныйgit add
Задание 4. Разрешение конфликта
Цель: пережить конфликт в безопасных условиях, чтобы не паниковать в боевых
Шаги:
printf 'app_name=MyApp\nversion=1.0\nauthor=User\n' > config.txt
git add config.txt
git commit -m "Добавить файл конфигурации"
git switch -c feature/update-version
sed -i 's/version=1.0/version=2.0/' config.txt
git commit -am "Поднять версию до 2.0"
git switch main
sed -i 's/version=1.0/version=1.5/' config.txt
git commit -am "Поднять версию до 1.5"
git merge feature/update-version # здесь будет конфликт
git status # покажет файл в состоянии both modified
cat config.txt # посмотри на маркеры конфликта
Внутри файла увидишь:
<<<<<<< HEAD
version=1.5 ← версия из ветки, в которой ты находишься (main)
=======
version=2.0 ← версия из ветки, которую вливаешь
>>>>>>> feature/update-version
Открой файл, оставь version=2.0, удали все три строки-маркера, затем:
git add config.txt
git commit # сообщение уже подставлено, просто сохрани
git log --oneline --graph
Ожидаемый вывод: появился коммит слияния, в файле осталась одна строка version=2.0
Типичные ошибки:
- Забыть удалить маркеры
<<<<<<<,=======,>>>>>>>— они останутся прямо в файле и сломают конфиг - Паника и
git merge --abortпри каждом конфликте. Эта команда полезна (она откатывает слияние целиком), но конфликт — норма, а не авария
Задание 5. Сквозной проект: «Заметки» в git и первый пайплайн
Цель: положить приложение из тем 1–2 в репозиторий и настроить автоматическую проверку при каждом изменении
Шаги:
-
Преврати каталог проекта в репозиторий:
cd ~/notes git init -
Создай
.gitignore— заметки пользователей и логи в репозиторий не кладём:notes.txt *.log __pycache__/ .env *.key *.crt -
Добавь
README.mdс описанием сервиса: что это, как запустить, какие переменные окружения понимает (PORT,NOTES_FILE), какие эндпоинты есть (/,/healthz). Это не формальность:README— первое, что читает человек, попавший в незнакомый репозиторий -
Скопируй в репозиторий конфиги, которые мы писали руками, — теперь они тоже часть проекта:
mkdir -p deploy sudo cp /etc/systemd/system/notes.service deploy/ sudo cp /etc/nginx/conf.d/notes.conf deploy/ sudo chown $USER deploy/* -
Первый коммит и отправка на GitHub:
git add . git status # убедись, что notes.txt НЕ попал в список git commit -m "Первая версия сервиса «Заметки»" git branch -M main git remote add origin git@github.com:<твой_логин>/notes.git git push -u origin main -
Добавь пайплайн
.github/workflows/ci.yml:name: CI # Когда запускать: при отправке в main и при любом Pull Request в main on: push: branches: [main] pull_request: branches: [main] jobs: check: runs-on: ubuntu-latest steps: # Скачать код репозитория на runner - uses: actions/checkout@v4 - name: Установить Python uses: actions/setup-python@v5 with: python-version: "3.12" # Самый дешёвый шаг — проверка синтаксиса. Идёт первым: fail fast - name: Проверить синтаксис приложения run: python -m py_compile app.py - name: Проверить стиль кода run: | pip install ruff ruff check app.py # Настоящий тест: поднимаем сервис и проверяем, что он отвечает - name: Проверить, что сервис отвечает run: | PORT=8080 python app.py & sleep 2 curl --fail --silent http://localhost:8080/healthz echo "healthcheck прошёл" - name: Проверить конфиг nginx run: | sudo apt-get update && sudo apt-get install -y nginx sudo cp deploy/notes.conf /etc/nginx/conf.d/ sudo nginx -t -
Отправь и посмотри результат:
git add .github/workflows/ci.yml git commit -m "Добавить пайплайн проверки" git pushОткрой GitHub → вкладка Actions. Разверни выполнившийся запуск и прочитай вывод каждого шага
-
Сломай специально. Создай ветку, внеси синтаксическую ошибку в
app.py(например, убери закрывающую скобку), открой Pull Request:git switch -c fix/broken-syntax # внеси ошибку в app.py git commit -am "Проверка: что будет при поломке" git push -u origin fix/broken-syntaxОткрой PR на GitHub и посмотри: проверка станет красной, а кнопка слияния — предупреждающей. Именно так CI защищает основную ветку. Почини ошибку, сделай новый коммит в ту же ветку — проверка перезапустится сама и станет зелёной
Ожидаемый вывод: зелёная галочка у коммитов в main; красный крест на PR с ошибкой; в логе шага видно, какая именно строка сломана
Типичные ошибки:
- Пайплайн не запускается вовсе — файл должен лежать строго в
.github/workflows/и иметь расширение.yml Invalid workflow file— почти всегда сбитые отступы в YAML или таб вместо пробелов- В шаге с запуском сервиса не хватает
sleep—curlстучится раньше, чем приложение успело подняться. Это первая встреча с проблемой, которую в теме 5 Kubernetes решит через readiness-пробы - Использовать старые версии действий (
actions/checkout@v2) — они работают на устаревшем окружении и однажды перестанут запускаться. Актуальная версия —v4
Проверь себя: тема освоена, если ты можешь
- Объяснить разницу между рабочим каталогом, индексом и историей
- Создать ветку, поработать в ней и влить через Pull Request
- Разрешить конфликт слияния руками и объяснить, что означают маркеры
- Объяснить, почему в общей ветке нельзя делать
push --forceиreset - Написать
.gitignoreи объяснить, почему пароль в истории — это инцидент - Написать с нуля пайплайн из нескольких шагов и объяснить их порядок
- Показать репозиторий «Заметок», где PR с ошибкой не проходит проверку