GitLab7 мин чтения2026-03-06

GitLab API Merge Request: пошаговый гайд для разработчика

Merge request в GitLab — это не просто способ слить код в основную ветку. Это целая система для контроля качества, код-ревью и автоматизации. А если ты работаеш

Merge request в GitLab — это не просто способ слить код в основную ветку. Это целая система для контроля качества, код-ревью и автоматизации. А если ты работаешь с API, то вообще открываются безграничные возможности для автоматизации процессов разработки.

Я много раз видел, как разработчики используют UI GitLab, кликают мышкой, создают merge requests вручную. Нормально, конечно, но когда надо автоматизировать процесс или интегрировать с внешними инструментами — начинают искать документацию и теряются. Давай разберёмся, что к чему.

Что такое merge request в GitLab и почему это важно

Merge request (MR) — это запрос на слияние одной ветки с другой. По сути, это то же самое, что pull request в GitHub, но GitLab свой термин использует. Если ты работал с GitHub — забудь привычное PR, здесь MR.

Merge request нужен для нескольких вещей:

Во-первых, это контроль качества. Перед тем как код попадёт в main или develop, его смотрит другой разработчик, оставляет замечания, просит изменить. Во-вторых, это триггер для CI/CD конвейера. Когда ты создаёшь MR, автоматически запускаются тесты, линтеры, статический анализ — все проверки, которые ты настроил. В-третьих, это документация. В MR ты оставляешь описание, почему ты делал эти изменения, какой проблеме это решает.

А если ты работаешь с API, то можешь автоматизировать весь этот процесс. Создавать MR программно, назначать ревьюеров, добавлять метки, мёржить автоматически когда все проверки пройдены — всё это из кода.

Установка и подготовка: python-gitlab и токен доступа

Чтобы работать с GitLab API из Python, используй библиотеку python-gitlab. Это официальная библиотека от GitLab, поддерживается хорошо.

pip install python-gitlab

Дальше тебе нужен токен доступа. Это как пароль, но для API. Заходишь в GitLab, в профиль, ищешь "Access Tokens" или "Personal Access Tokens", создаёшь новый. Нужны права:

Вот как это выглядит в коде:

import gitlab

# Подключаемся к GitLab
gl = gitlab.Gitlab('https://gitlab.com', private_token='your-token-here')

# Или если у тебя свой GitLab на сервере
gl = gitlab.Gitlab('https://gitlab.company.com', private_token='your-token-here')

# Проверяем, что всё работает
gl.auth()

Если ты работаешь на Linux или Mac, токен лучше хранить в переменной окружения, а не в коде:

import os
import gitlab

token = os.getenv('GITLAB_TOKEN')
gl = gitlab.Gitlab('https://gitlab.com', private_token=token)

А в .env файл пишешь:

GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxx

И не забудь добавить .env в .gitignore, чтобы случайно не залить токен в репозиторий.

Создание merge request через API

Теперь к делу. Как создать merge request из кода?

import gitlab

gl = gitlab.Gitlab('https://gitlab.com', private_token='your-token')
project = gl.projects.get('project-id-or-path')

# Создаём merge request
mr = project.mergerequests.create({
    'source_branch': 'feature/my-new-feature',
    'target_branch': 'main',
    'title': 'Add new user authentication',
    'description': 'Implements JWT-based authentication for the API'
})

print(f"Merge request created: {mr.web_url}")

Если ты не знаешь ID проекта, можешь передать path — он тоже работает. Например, 'my-group/my-project'.

А вот более реальный пример. На одном проекте мы автоматизировали создание MR для обновления зависимостей:

def create_dependency_update_mr(project, old_version, new_version, package_name):
    """Создаёт MR для обновления зависимости"""
    
    branch_name = f"chore/update-{package_name}-{new_version}"
    
    # Сначала проверяем, не существует ли уже такая ветка
    try:
        project.branches.get(branch_name)
        print(f"Branch {branch_name} already exists")
        return None
    except gitlab.exceptions.GitlabGetError:
        pass  # Ветка не существует, продолжаем
    
    # Создаём новую ветку от main
    branch = project.branches.create({
        'branch': branch_name,
        'ref': 'main'
    })
    
    # Создаём merge request
    mr = project.mergerequests.create({
        'source_branch': branch_name,
        'target_branch': 'main',
        'title': f'Bump {package_name} from {old_version} to {new_version}',
        'description': f'''
Автоматическое обновление зависимости.

- **Package**: {package_name}
- **From**: {old_version}
- **To**: {new_version}

/label dependencies
/assign @devops-team
        '''
    })
    
    return mr

Видишь, я добавил слеш-команды в описание? /label dependencies — это добавит метку, /assign @devops-team — назначит ревьюеров. Очень удобно.

Работа с существующими merge requests

Иногда нужно не создать MR, а работать с уже существующим. Например, добавить комментарий, изменить статус, добавить метку.

# Получаем конкретный MR
mr = project.mergerequests.get(1)  # 1 — это номер MR

# Смотрим базовую информацию
print(f"Title: {mr.title}")
print(f"Status: {mr.state}")
print(f"Author: {mr.author['name']}")
print(f"Approvals: {mr.approvals_before_merge}")

# Добавляем комментарий
mr.notes.create({
    'body': 'Looks good! Just need to add tests for the new function.'
})

# Одобряем MR (если у тебя есть права)
mr.approve()

# Добавляем метку
mr.labels.append('reviewed')
mr.save()

# Назначаем ревьюера
mr.assignees = [{'id': 123}]  # ID ревьюера
mr.save()

Если честно, самая полезная штука — это добавление комментариев. Можешь настроить автоматический код-ревью, которое будет оставлять замечания прямо в MR. На этом, кстати, построен Distiq — AI-бот, который анализирует каждый MR и оставляет инлайн-комментарии с замечаниями о стиле, безопасности и производительности.

Интеграция с CI/CD и автоматическое мёржение

Самая мощная штука — это интеграция MR с CI/CD конвейером. В .gitlab-ci.yml можешь настроить правила, когда MR мёржится автоматически.

Вот конфиг для проекта, где мы мёржим MR автоматически, если все тесты и проверки пройдены:

stages:
  - test
  - review
  - merge

test:
  stage: test
  image: python:3.11
  script:
    - pip install -r requirements.txt
    - pytest --cov=src --cov-report=term
  coverage: '/TOTAL.*\s+(\d+%)$/'

lint:
  stage: test
  image: python:3.11
  script:
    - pip install flake8 black isort
    - black --check src/
    - isort --check-only src/
    - flake8 src/

security:
  stage: review
  image: python:3.11
  script:
    - pip install bandit
    - bandit -r src/ -f json -o bandit-report.json
  artifacts:
    reports:
      sast: bandit-report.json
  allow_failure: true

auto_merge:
  stage: merge
  image: python:3.11
  script:
    - pip install python-gitlab
    - python scripts/auto_merge.py
  only:
    - merge_requests
  when: on_success

А вот скрипт, который мёржит MR если всё хорошо:

# scripts/auto_merge.py
import gitlab
import os

gl = gitlab.Gitlab(os.getenv('CI_SERVER_URL'), 
                  private_token=os.getenv('CI_JOB_TOKEN'))

project = gl.projects.get(os.getenv('CI_PROJECT_ID'))
mr = project.mergerequests.get(os.getenv('CI_MERGE_REQUEST_IID'))

# Проверяем, что все проверки прошли
if mr.merge_status == 'can_be_merged':
    # Мёржим с удалением исходной ветки
    mr.merge(
        should_remove_source_branch=True,
        squash=False
    )
    print(f"Merged MR !{mr.iid}")
else:
    print(f"Cannot merge: {mr.merge_status}")

Это очень полезно для автоматизации. Например, для обновления зависимостей, генерации документации или автоматического мёржа hotfix-ветвей в production.

Удаление merge request и управление ветками

Иногда нужно закрыть или удалить MR. Удалить нельзя полностью (GitLab так не позволяет), но можешь закрыть:

# Закрываем MR
mr.state_event = 'close'
mr.save()

# Или через метод
mr.close()

# Переоткрываем MR если нужно
mr.state_event = 'reopen'
mr.save()

Если нужно удалить исходную ветку, которую создал для MR:

# Удаляем ветку
branch = project.branches.get('feature/my-feature')
branch.delete()

# Или сразу при мёрже
mr.merge(should_remove_source_branch=True)

На практике мы часто используем такой подход: создаём MR, если он не был одобрен за 3 дня, закрываем его автоматически и удаляем ветку. Экономит место в репозитории.

from datetime import datetime, timedelta

# Ищем старые MR
open_mrs = project.mergerequests.list(state='opened')

for mr in open_mrs:
    created_at = datetime.fromisoformat(mr.created_at.replace('Z', '+00:00'))
    age = datetime.now(created_at.tzinfo) - created_at
    
    if age > timedelta(days=3):
        print(f"Closing old MR !{mr.iid}: {mr.title}")
        mr.close()
        
        # Удаляем ветку
        try:
            branch = project.branches.get(mr.source_branch)
            branch.delete()
        except:
            pass

Практические советы и типичные ошибки

Вот что я узнал на своём опыте:

Используй iid вместо id. В GitLab есть два разных идентификатора для MR: id (глобальный в системе) и iid (номер MR в проекте, тот, который ты видишь в UI). В API лучше использовать iid.

# Правильно
mr = project.mergerequests.get(1, lazy=True)

# Неправильно (работает, но медленнее)
mr = project.mergerequests.list(all=True)
mr = [m for m in mr if m.id == 123][0]

Кэшируй информацию о проекте. Если ты работаешь с одним проектом, получи его один раз и переиспользуй:

# Плохо: каждый раз запрашиваем проект
for i in range(10):
    mr = gitlab.Gitlab(...).projects.get(id).mergerequests.get(i)

# Хорошо: один раз получили, дальше используем
gl = gitlab.Gitlab(...)
project = gl.projects.get(id)
for i in range(10):
    mr = project.mergerequests.get(i)

Обрабатывай исключения. API может быть недоступен, MR может быть удалён, может быть проблема с правами доступа:

try:
    mr = project.mergerequests.get(1)
    mr.approve()
except gitlab.exceptions.GitlabGetError:
    print("MR not found")
except gitlab.exceptions.GitlabUpdateError:
    print("Cannot update MR (probably no permissions)")
except gitlab.exceptions.GitlabAuthError:
    print("Authentication failed")

Не создавай MR без проверки. Перед созданием MR проверь, что ветка существует и что в ней есть изменения:

def safe_create_mr(project, source, target, title):
    # Проверяем, что ветки существуют
    try:
        project.branches.get(source)
        project.branches.get(target)
    except gitlab.exceptions.GitlabGetError as e:
        print(f"Branch not found: {e}")
        return None
    
    # Проверяем, что нет уже открытого MR между этими ветками
    existing = project.mergerequests.list(
        source_branch=source,
        target_branch=target,
        state='opened'
    )
    
    if existing:
        print(f"MR already exists: {existing[0].web_url}")
        return existing[0]
    
    # Создаём новый MR
    return project.mergerequests.create({
        'source_branch': source,
        'target_branch': target,

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи