/
Вопросы и ответы
/
Промт-инжиниринг
/

LLM для работы с Git: commit messages, changelog, ревью PR

LLM для работы с Git: commit messages, changelog, ревью PR

9 часов назад

Никита Вихров

Ответы

0

LLM для работы с Git: commit messages, changelog, ревью PR

Git-рутина отнимает время и внимание. Написать хороший commit message, собрать changelog из сотни коммитов, описать PR — всё это ИИ делает быстро и лучше среднего разработчика в конце рабочего дня.


Генерация commit messages

from anthropic import Anthropic
import subprocess

client = Anthropic()


def get_staged_diff() -> str:
    """Получает diff из staged изменений"""
    result = subprocess.run(
        ["git", "diff", "--cached"],
        capture_output=True, text=True
    )
    return result.stdout


def generate_commit_message(diff: str, style: str = "conventional") -> str:
    styles = {
        "conventional": """Conventional Commits: <type>(<scope>): <description>
Типы: feat, fix, docs, style, refactor, test, chore
Пример: feat(auth): add JWT token refresh endpoint""",
        "imperative": "Повелительное наклонение: 'Add feature', 'Fix bug', 'Update docs'",
        "descriptive": "Полное описание что и зачем изменено",
    }

    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=256,
        system=f"""Пишешь commit messages в стиле: {styles[style]}

Правила:
- Первая строка: краткое описание (до 72 символов)
- Если нужно — пустая строка и подробности
- Только по делу, без воды
- Возвращай только commit message""",
        messages=[{
            "role": "user",
            "content": f"```diff\n{diff[:4000]}\n```"  # обрезаем большие диффы
        }]
    )
    return response.content[0].text.strip()


# Использование как git hook или CLI
diff = get_staged_diff()
if diff:
    message = generate_commit_message(diff)
    print(f"Предлагаемый commit message:\n{message}")

    # Можно сразу применить
    # subprocess.run(["git", "commit", "-m", message])
else:
    print("Нет staged изменений")

Генерация changelog

def get_commits_since(tag: str) -> list[dict]:
    """Получает коммиты с момента последнего тега"""
    result = subprocess.run(
        ["git", "log", f"{tag}..HEAD", "--pretty=format:%H|%s|%an|%ad", "--date=short"],
        capture_output=True, text=True
    )

    commits = []
    for line in result.stdout.strip().split("\n"):
        if line:
            hash_, subject, author, date = line.split("|", 3)
            commits.append({"hash": hash_[:7], "subject": subject, "author": author, "date": date})

    return commits


def generate_changelog(commits: list[dict], version: str) -> str:
    commits_text = "\n".join([f"- {c['hash']} {c['subject']} ({c['author']})" for c in commits])

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        system="""Пишешь CHANGELOG.md в формате Keep a Changelog.
Группируй изменения: Added, Changed, Fixed, Removed, Security.
Пиши для конечного пользователя — без технического жаргона.
Объединяй связанные коммиты в одну запись.""",
        messages=[{
            "role": "user",
            "content": f"""Версия: {version}
Коммиты:
{commits_text}

Напиши секцию changelog для этой версии."""
        }]
    )
    return response.content[0].text


# Генерируем changelog для нового релиза
commits = get_commits_since("v1.2.0")
changelog = generate_changelog(commits, "v1.3.0")
print(changelog)

# Добавляем в начало CHANGELOG.md
with open("CHANGELOG.md", "r+") as f:
    existing = f.read()
    f.seek(0)
    f.write(changelog + "\n\n" + existing)

Описание Pull Request

def generate_pr_description(
    base_branch: str = "main",
    pr_title: str = ""
) -> str:

    # Получаем diff и список коммитов
    diff = subprocess.run(
        ["git", "diff", base_branch, "--stat"],
        capture_output=True, text=True
    ).stdout

    commits = subprocess.run(
        ["git", "log", f"{base_branch}..HEAD", "--pretty=format:%s"],
        capture_output=True, text=True
    ).stdout

    full_diff = subprocess.run(
        ["git", "diff", base_branch],
        capture_output=True, text=True
    ).stdout[:6000]  # обрезаем для контекста

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        system="""Пишешь описание Pull Request в Markdown.
Структура:
## Что сделано
Краткое описание изменений.

## Зачем
Контекст и мотивация.

## Как проверить
Шаги для ревьюера.

## Скриншоты / примеры (если применимо)
[если есть UI изменения]

## Checklist
- [ ] Тесты добавлены/обновлены
- [ ] Документация обновлена
- [ ] Breaking changes задокументированы""",
        messages=[{
            "role": "user",
            "content": f"""Заголовок PR: {pr_title}

Изменённые файлы:
{diff}

Коммиты:
{commits}

Diff (фрагмент):
```diff
{full_diff}
```"""
        }]
    )
    return response.content[0].text


pr_desc = generate_pr_description("main", "feat: добавить авторизацию через Google OAuth")
print(pr_desc)

Автоматизация через git hooks

#!/bin/bash
# .git/hooks/prepare-commit-msg

COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2

# Только для обычных коммитов (не merge, не amend)
if [ -z "$COMMIT_SOURCE" ]; then
    DIFF=$(git diff --cached)
    
    if [ -n "$DIFF" ]; then
        SUGGESTED=$(python3 scripts/generate_commit.py "$DIFF")
        
        # Добавляем предложение в файл сообщения (закомментированным)
        echo "" >> "$COMMIT_MSG_FILE"
        echo "# Предложение ИИ:" >> "$COMMIT_MSG_FILE"
        echo "$SUGGESTED" | sed 's/^/# /' >> "$COMMIT_MSG_FILE"
    fi
fi

На курсе «ИИ для разработчиков» на Хекслете разбирают как встроить ИИ в каждый этап рабочего процесса разработчика — от написания кода до деплоя.

9 часов назад

Никита Вихров

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845