---
title: 'Хигиена на Git и монорепозитории: атомарни коммити, ребейз и CI/CD за микроуслуги'
description: 'Подробно ръководство за разработчици относно хигиената на Git, атомарните коммити, Conventional Commits, сравнение между merge и rebase, интерактивен ребейз, монорепозитории срещу полирепозитории и оптимизация на CI/CD.'
faq:
    - { question: 'Каква є основната полза от атомарните коммити?', answer: "Атомарните коммити гарантират, че всеки коммит съдържа точно една логическа промяна и съответните тестове. Това улеснява прегледа на кода, поддържа историята чиста, позволява лесно отменяне на конкретни промени и прави дебъгването с инструменти като 'git bisect' изключително ефективно." }
    - { question: 'Кога трябва да използвам merge и кога rebase?', answer: 'Използвайте rebase на локални, частни клонове за почистване на историята, обединяване на временни WIP коммити и поддържане на линейна история преди интеграция. Използвайте merge при сливане на готови клонове с функции в публични споделени клонове (като main или develop), за да запазите реалния хронологичен ред и да избегнете пренаписване на споделената история.' }
    - { question: 'Как мога да избегна стартирането на тестове за всички услуги в монорепозиторий при промяна само в една услуга?', answer: "Можете да оптимизирате своя CI конвейер чрез филтриране по пътища (например филтъра 'paths' в GitHub Actions или 'rules:changes' в GitLab CI), така че работният процес да се задейства само когато се променят файлове в поддиректорията на конкретната услуга." }
published: '2026-06-15'
---
# Хигиена на Git и монорепозитории: атомарни коммити, ребейз и CI/CD за микроуслуги

Представете си следното: петък следобед є и в продукционната среда възниква критичен бъг. Поглеждате историята на git, за да намерите промяната, доvela до грешката, но виждате само стена от коммити: „wip“, „fix typo“, „test“, „hope this works“ и огромна, заплетена паяжина от merge коммити. Намирането на проблемния коммит се превръща в търсене на игла в купа сено. В друг проект разработчик променя един ред текст в readme файл на микроуслуга, което задейства 40-минутен CI/CD конвейер, който пренастройва и деплоява отново всички двадесет микроуслуги в системата. Тези проблеми не са дефекти в технологичния стек, а липса на стратегия за контрол на версиите и архитектура на репозиториите.

Чистите стандарти за коммити и оптимизираните конфигурации на монорепозитории са от съществено значение за изграждането на надеждни, мащабируеми и удобни за разработчиците процеси в микроуслужната архитектура.

## Съдържание
* [Хигиена на коммитите и силата на атомарността](#git-hygiene)
* [Стратегии за интеграция: Merge срещу Rebase](#merge-vs-rebase)
* [Продължителни операции в Git: интерактивен ребейз и Reflog](#advanced-git)
* [Организация на микроуслуги: монорепозитории срещу полирепозитории](#monorepo-vs-polyrepo)
* [Инструменти за монорепозитории за PHP и Laravel](#php-monorepos)
* [Оптимизация на CI/CD конвейерите в монорепозитории](#monorepo-ci)
* [Ограничения и компромиси](#limitations)
* [Практически изводи](#takeaways)

---

<a id="git-hygiene"></a>
## Хигиена на коммитите и силата на атомарността

Системата за контрол на версиите є повече от просто средство за архивиране; тя є инструмент за комуникация и документиране на историята на проекта.

### Атомарни коммити
* **Теза**: Коммитът трябва да бъде възможно най-малката логическа единица код, която є завършена, работеща и съдържа промяната заедно с тестовете.
* **Защо є важно**: Атомарният коммит може лесно да бъде отменен (revert) без риск от счупване на други функции. Освен това прави командата `git bisect` (търсене на бъгове чрез двоичен алгоритъм в историята) максимално ефективна.
* **Пример**: Вместо да обединявате рефакторинг, коригиране на бъг и актуализиране на зависимости в един огромен коммит, ги разделете на три отделни коммита.
* **Последствия**: Прегледите на кода стават по-бързи, историята на промените остава чиста, а отмяната на версии є безболезнена.

### Conventional Commits (Споразумение за коммити)
За стандартизиране на съобщенията се използва спецификацията **Conventional Commits**. Съобщенията се пишат по шаблон: `<type>(<scope>): <description>`.
* `feat`: Нова функционалност.
* `fix`: Коригиране на бъгове.
* `chore`: Рутинни задачи, актуализиране на зависимости, конфигурация на сглобяване.
* `docs`: Промени в документацията.
* `refactor`: Промени в кода, които нито коригират бъг, нито добавят функция.
* `test`: Добавяне или коригиране на тестове.

---

<a id="merge-vs-rebase"></a>
## Стратегии за интеграция: Merge срещу Rebase

Изборът как да интегрирате историята на клоновете в основния клон определя четливостта и структурата на целия репозиторий.

```
Merge (Нелинейна история):
A --- B --- C (main)
 \         /
  D --- E (feature)

Rebase (Линейная история):
A --- B --- C --- D' --- E' (main/feature)
```

### Git Merge
* **Теза**: Обединява клонове чрез създаване на нов „merge коммит“, който има няколко родителя.
* **Защо є важно**: Запазва точната хронология на събитията и отразява реалната история на това кога клоновете са се разделили и сляли.
* **Последствия**: Историята става нелинейна и се претоварва с коммити от рода на \"Merge branch 'main' into feature\", което затруднява нейното четене.

### Git Rebase
* **Теза**: Премества базовия коммит на вашия клон с функция върху най-новия коммит на целевия клон, пренареждайки вашите коммити върху него.
* **Защо є важно**: Създава напълно линейна история, при която всички промени изглеждат така, сякаш са разработени последователно.
* **Последствия**: Тази команда пренаписва хешовете на коммитите. Ако направите ребейз на публичен споделен клон, това ще предизвика конфликти за всички останали разработчици в екипа. **Правило: Никога не правете rebase на споделени клонове.**

---

<a id="advanced-git"></a>
## Продължителни операции в Git: интерактивен ребейз и Reflog

Разширените команди в Git помагат за почистване на локалния код, преди той да бъде споделен с екипа.

### Интерактивен ребейз (`git rebase -i`)
* **Теза**: Позволява ви да пренапишете, обедините, пренаредите или изтриете коммити в историята на локалния клон преди изпращане.
* **Защо є важно**: Помага за изчистване на временни коммити като „wip“ и „fix typo“, представяне на чиста история в pull request и запазване на атомарността.
* **Пример**: Командата `git rebase -i HEAD~4` ще отвори списък с последните 4 коммита:
  ```text
  pick a1b2c3d feat(auth): add google oauth provider
  squash d4e5f6g wip oauth login
  squash h7i8j9k fix typo in redirect url
  reword l0m1n2o feat(auth): add docs for oauth integration
  ```
  Това ще обедини (squash) междинните малки корекции в основния коммит за функцията и ще пренапише крайното съобщение.

### Cherry-Picking (`git cherry-pick`)
* **Теза**: Пренася конкретен коммит от един клон в настоящия клон.
* **Защо є важно**: Полезно за бързо пренасяне на критична корекция на бъг в продукционния клон, без да се слива целият клон с функции, съдържащ корекцията.

### Git Reflog (`git reflog`)
* **Теза**: Локален дневник, който записва абсолютно всички промени на указателите на клоновете, дори ако коммитите са били изтрити или загубени при ребейз.
* **Защо є важно**: Ако направите грешка по време на ребейз и загубите код, `git reflog` ще покаже оригиналния хеш на коммита, който може да бъде възстановен с командата `git reset --hard <hash>`.

---

<a id="monorepo-vs-polyrepo"></a>
## Организация на микроуслуги: монорепозитории срещу полирепозитории

При проектирането на микроуслуги възниква въпросът за организацията на репозиториите за код.

### Полирепозиторий (Polyrepo — репозиторий за всяка услуга)
* **Теза**: Всяка микроуслуга живее в свой изолиран репозиторий.
* **Защо є важно**: Ясни граници на отговорност, малък размер за клониране на код и напълно изолирани CI/CD конвейери.
* **Последствия**: Споделянето на общ код є затруднено (изисква се публикуване на пакети), трудно се правят крос-услужни промени, а версиите на споделените зависимости бързо се десинхронизират.

### Монорепозиторий (Monorepo — един репозиторий за всичко)
* **Теза**: Всички микроуслуги, библиотеки и шлюзове живеят в един общ репозиторий.
* **Защо є важно**: Лесно внасяне на сквозни промени, еднакви версии на библиотеките и мигновен споделен достъп до кода без публикуване на външни пакети.
* **Последствия**: Огромен размер на репозитория, сложна конфигурация на CI/CD и риск от размиване на границите, когато услугите започнат неявно да се свързват през общи папки.

---

<a id="php-monorepos"></a>
## Инструменти за монорепозитории за PHP и Laravel

За разлика от JavaScript, където са популярни среди като Turborepo или Lerna, разработчиците на PHP могат ефективно да изграждат монорепозитории с помощта на вградените функции на Composer.

### 1. Репозитории за пътища на Composer (Path Repositories)
Вместо да публикувате споделени библиотеки в Packagist, можете да реферирате към локални папки чрез `path` репозитории в `composer.json` на микроуслугата:
```json
{
    "repositories": [
        {
            "type": "path",
            "url": "../packages/shared-dto",
            "options": {
                "symlink": true
            }
        }
    ],
    "require": {
        "devsense/shared-dto": "*"
    }
}
```
Composer ще създаде символна връзка (symlink) към локалната библиотека. Това позволява да променяте общия код и мигновено да виждате промените в микроуслугата без изпълнение на `composer update`.

### 2. Monorepo Builder
Инструменти като **Monorepo Builder** помагат за обединяване на конфигурации `composer.json`, автоматизиране на семантичното версиониране на подпакети и синхронизиране на версиите на външните зависимости във всички услуги.

---

<a id="monorepo-ci"></a>
## Оптимизация на CI/CD конвейерите в монорепозитории

Най-тясното място на монорепозитория є времето за сглобяване. Ако всеки коммит задейства тестове за всички микроуслуги, CI/CD става бавен и скъп.

* **Теза**: Стартираме етапите на CI/CD избирателно с помощта на филтриране на пътища.
* **Защо є важно**: Спестява ресурси на сървърите и ускорява доставката на код.
* **Пример**: В GitHub Actions можете да настроите стартиране на работен процес само при промяна на файлове в определени папки:
  ```yaml
  # .github/workflows/user-service.yml
  on:
    push:
      branches: [ main ]
      paths:
        - 'services/user-service/**'
        - 'packages/shared-dto/**' # Стартиране, ако общите зависимости се променят
  ```
* **Последствия**: Тества се и се сглобява само променената услуга и нейните преки зависимости, което съкращава времето за CI от 30 на 2 минути.

---

<a id="code-demo"></a>
## Практически пример за код

Ето реални конфигурационни шаблони за настройка на работния процес.

### 1. Процес на интерактивен ребейз в Git
За почистване на локалната история преди изпращане на клона:
```bash
# 1. Стартиране на интерактивен ребейз за последните 3 коммита
git rebase -i HEAD~3

# 2. В отворения редактор заменете 'pick' с 'squash' (или 's') за междинните коммити:
# pick 82a17f2 feat: add database indexing
# squash d928f01 fix syntax error in migration
# squash a19f291 add missing index fields

# 3. Запазете промените. Git ще предложи да редактирате обединеното съобщение:
# feat: add database indexing and migrations
```

### 2. Настройка на избирателен CI за монорепозиторий (GitHub Actions)
```yaml
# .github/workflows/checkout-service.yml
name: Checkout Service CI

on:
  push:
    branches: [ main, development ]
    paths:
      - 'services/checkout-service/**'
      - 'packages/shared-kernel/**'
  pull_request:
    branches: [ main, development ]
    paths:
      - 'services/checkout-service/**'
      - 'packages/shared-kernel/**'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.5'
          extensions: mbstring, xml, bcmath, pdo_pgsql

      - name: Install Dependencies
        run: |
          cd services/checkout-service
          composer install --no-interaction --prefer-dist --optimize-autoloader

      - name: Run Tests
        run: |
          cd services/checkout-service
          ./vendor/bin/phpunit
```

---

<a id="limitations"></a>
## Ограничения и компромиси

* **Опасност от ребейз**: Ребейзът пренаписва историята на коммитите. Форсираното изпращане (`git push --force`) на споделен клон след ребейз ще изтрие коммитите на колегите. За безопасност винаги използвайте `git push --force-with-lease`.
* **Мащаб на монорепозитория**: С нарастването на монорепозитория командите `git status` и `git fetch` могат да се забавят. Големите медийни файлове трябва да се съхраняват чрез Git LFS (Large File Storage) или напълно да се изнасят извън Git.
* **Сложност на CI/CD**: Ръчното управление на зависимостите на пътищата в CI може да доведе до бъгове, ако промяна в обща библиотека не задейства тестове в зависима услуга. За управление на графа на сглобяване в големи проекти є препоръчително да използвате Turborepo или Nx.

---

<a id="takeaways"></a>
## Практически изводи

1. **Пишете атомарни коммити**: Една логическа промяна — един коммит. Пишете съобщения в повелително наклонение (например, `feat(auth): add token verification`, а не `added verification`).
2. **Локално — Rebase, публично — Merge**: Дръжте клоновете линейни чрез `git rebase`, но ги сливайте в общи клонове чрез явни merge коммити (`git merge --no-ff`), за да запазите точките на интеграция.
3. **Използвайте Path Repositories за PHP**: Свързвайте споделени пакети чрез символни връзки за мигновено тестване на промените на локалната машина.
4. **Внедрявайте филтриране по пътища в CI**: Предотвратявайте излишно стартиране на тестове, като насочвате конвейерите само към променените папки.
5. **Използвайте `git push --force-with-lease`**: Никога не правете push вслепую, защитавайте отдалечената работа на колегите си от случайно пренаписване.