gmr - Git Merge Request automation

CLI-утиліта на Go, яка автоматизує створення Merge Request / Pull Request:
стейджить зміни, генерує commit message через AI (Gemini / Claude / OpenAI),
створює або повторно використовує feature-гілку та відкриває GitLab MR або
GitHub PR. Окремі підкоманди створюють релізи (gmr deploy) і показують стан
CI/CD (gmr status). Платформа визначається автоматично за URL origin remote.
Installation
Pre-built binary для Linux/macOS (рекомендовано)
Завантажити архів для вашої ОС / архітектури з GitHub Releases:
VERSION=$(curl -fsSL https://api.github.com/repos/slucheninov/gmr/releases/latest | jq -r .tag_name)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
ARCHIVE="gmr-${VERSION}-${OS}-${ARCH}.tar.gz"
curl -LO "https://github.com/slucheninov/gmr/releases/download/${VERSION}/${ARCHIVE}"
tar -xzf "${ARCHIVE}"
sudo install -m 0755 gmr /usr/local/bin/gmr
gmr --version
Контрольні суми (checksums.txt) додаються до кожного релізу:
curl -L -O "https://github.com/slucheninov/gmr/releases/download/${VERSION}/checksums.txt"
CHECKSUM=$(awk -v archive="${ARCHIVE}" '$2 == archive { print $1 }' checksums.txt)
test -n "${CHECKSUM}"
printf '%s %s\n' "${CHECKSUM}" "${ARCHIVE}" | shasum -a 256 -c -
Цей install-приклад використовує curl, jq, awk і shasum; вони потрібні
лише для завантаження та перевірки архіву, але не для роботи gmr.
Pre-built binary для Windows
Завантаж gmr-<VERSION>-windows-amd64.zip або
gmr-<VERSION>-windows-arm64.zip зі сторінки
GitHub Releases, звір
SHA-256 із checksums.txt, розпакуй gmr.exe і додай його теку до PATH.
Через go install
go install github.com/slucheninov/gmr/cmd/gmr@latest
Якщо go env GOBIN порожній, бінарник буде у $(go env GOPATH)/bin;
інакше — у явно налаштованому GOBIN. Переконайся, що ця тека є в PATH.
З вихідного коду
git clone https://github.com/slucheninov/gmr.git
cd gmr
go build -o gmr ./cmd/gmr
sudo install -m 0755 gmr /usr/local/bin/gmr
Update
Pre-built binary для Linux/macOS
VERSION=$(curl -fsSL https://api.github.com/repos/slucheninov/gmr/releases/latest | jq -r .tag_name)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
ARCHIVE="gmr-${VERSION}-${OS}-${ARCH}.tar.gz"
curl -LO "https://github.com/slucheninov/gmr/releases/download/${VERSION}/${ARCHIVE}"
tar -xzf "${ARCHIVE}"
sudo install -m 0755 gmr /usr/local/bin/gmr
gmr --version
Через go install
go install github.com/slucheninov/gmr/cmd/gmr@latest
З вихідного коду
cd gmr
git pull
go build -o gmr ./cmd/gmr
sudo install -m 0755 gmr /usr/local/bin/gmr
Runtime requirements
git.
- GitLab CLI
glab для GitLab або
GitHub CLI gh для GitHub; відповідний CLI має
бути авторизований через glab auth login або gh auth login.
GEMINI_API_KEY, ANTHROPIC_API_KEY та/або OPENAI_API_KEY, коли потрібно
згенерувати commit message. Уже закомічена feature-гілка та gmr status не
потребують AI-ключа; gmr deploy може використати fallback без AI.
EDITOR потрібен лише для інтерактивної команди e(edit).
Usage
gmr [options] [branch-name] # full flow: commit + MR/PR
gmr -m # generate commit message, then ask to commit it
gmr -c # commit and push to the current branch, no MR/PR
gmr -s # after MR/PR, stay on the feature branch
gmr -h # help
gmr -v # version
gmr deploy [options] [tag] # cut and publish the next release tag
gmr status [options] [ref] # show CI/CD pipeline status
deploy і status - зарезервовані слова: якщо вони передані першим аргументом, gmr вважає це підкомандою, а не назвою гілки.
Якщо branch-name не вказано, назва гілки виводиться з AI commit title (наприклад, fix-detect, feat-add). При колізії додається числовий суфікс (fix-detect2). Fallback: auto-YYYYMMDD-HHMMSS.
Якщо gmr запущено з уже створеної feature-гілки, яка має коміти відносно основної гілки, він використовує її як source branch і одразу створює MR/PR. Нова гілка та новий коміт не створюються, AI API key не потрібен, а після завершення (або помилки) gmr залишається на поточній feature-гілці. Якщо в ній є незакомічені зміни, вони спочатку комітяться у цю ж гілку.
Якщо gmr запущено з основної гілки без незакомічених змін, але в ній є коміти, ще не запушені в origin (наприклад, після gmr -m, яка закомітила прямо в основну гілку), gmr не завершується помилкою: він переносить ці коміти в нову гілку, відкриває MR/PR, а після успіху скидає локальну основну гілку на origin/<main>. Якщо при цьому є ще й незакомічені зміни, новий коміт і вже наявні незапушені коміти потрапляють в один MR/PR разом, і основна гілка так само скидається на origin/<main> після успіху.
З прапорцем -m (--message) утиліта стейджить усі зміни через git add -A,
генерує commit message через AI і завжди виводить його у stdout (тому
gmr -m | ... можна пайпити). Далі запитує
Commit to '<current-branch>'? [Y/n/e(edit)] (n = print only): -
y/yes/Enter комітить повідомлення в поточну гілку (без нової гілки, push
чи MR/PR) і показує блок "Next steps" з готовими командами git push /
gh pr create / glab mr create / gmr; n/no (і будь-яка інша
відповідь - безпечний варіант за замовчуванням) лише друкує повідомлення,
зміни лишаються застейдженими (git reset для розстейджування); e/edit
відкриває $EDITOR перед комітом. Якщо stdin не інтерактивний термінал,
питання не задається і gmr поводиться як при відповіді n; так само
трактується EOF (Ctrl+D або stdin з /dev/null). Працює з
будь-якої гілки.
З прапорцем -c (--commit) утиліта комітить і пушить у поточну гілку без
створення нової гілки та MR/PR (gh/glab не потрібні). Як і -m, вона
стейджить усі зміни, генерує commit message, друкує його у stdout і запитує
Commit and push to '<current-branch>'? [Y/n/e(edit)] (n = print only): -
y/yes/Enter комітить і виконує git push -u origin <branch>; e/edit
відкриває $EDITOR перед комітом; n/no, будь-яка інша відповідь або
неінтерактивний stdin лише друкують повідомлення. Якщо незакомічених змін
немає, AI не викликається: gmr -c просто пушить коміти, яких ще немає в
origin (або саму гілку, якщо її там ще немає), а якщо пушити нічого -
завершується помилкою. На основній гілці виводиться попередження, що push
піде в неї напряму. Не поєднується з -m, -s і branch-name.
З прапорцем -s (--stay) після успішного створення MR/PR ти залишаєшся на feature-гілці без жодних питань; без прапорця gmr запитає Stay on branch '<branch>' or switch to '<main>'? [s/M]: - s/stay/y/yes (без урахування регістру) залишає на гілці, будь-яка інша відповідь або Enter перемикає на основну гілку і робить git pull. Якщо stdin не є інтерактивним терміналом, питання пропускається і gmr одразу перемикається на основну гілку.
How it works
- Визначає поточну гілку: з основної запускає повний flow зі створенням feature-гілки, а на існуючій feature-гілці використовує вже додані коміти.
- Визначає платформу (GitLab / GitHub) за URL
origin remote.
- Якщо є незакомічені зміни, стейджить їх (
git add -A).
- Для нових змін генерує commit message через AI: Gemini → Claude → OpenAI → ручне введення. Для вже закоміченої feature-гілки використовує повідомлення останнього коміту.
- З основної гілки створює нову feature-гілку й коміт; з існуючої feature-гілки використовує її напряму. Потім відкриває MR (
glab) або PR (gh).
- Для GitLab передає в
glab явні title і description: використовує body commit message, а якщо його немає - генерує короткий ## Summary із заголовка коміту.
- Для GitHub - вмикає auto-merge зі squash (gracefully degrade, якщо репо це забороняє).
- Для нового flow запитує, залишитись на feature-гілці чи перемкнутись на основну (з
-s / --stay питання пропускається і gmr одразу залишається на гілці); за замовчуванням (Enter або інша відповідь) перемикається на основну гілку і виконує git pull. При запуску з існуючої feature-гілки завжди залишається на ній без питань.
gmr deploy
Створює наступний semver-тег із комітів з моменту попереднього тега, генерує release notes і semver bump через AI, пушить тег і створює GitHub Release / GitLab Release.
gmr deploy # AI обирає bump (patch/minor/major) і пише release notes
gmr deploy --minor # форсувати minor bump замість вибору AI
gmr deploy v1.4.0 # явний тег - перекриває будь-який bump
gmr deploy --no-release # створити і запушити тег, але не створювати Release
gmr deploy -y # без підтвердження
Правила визначення тега:
- Якщо в репозиторії ще немає semver-тегів - перший реліз
v0.0.1 (префікс з GMR_TAG_PREFIX, за замовчуванням v).
- Інакше - найновіший тег, збільшений на рівень bump (
--patch/--minor/--major або вибір AI), з тим самим префіксом.
- Явний позиційний тег (наприклад
v1.2.3) перекриває і прапорець, і вибір AI; має відповідати <prefix>MAJOR.MINOR.PATCH.
Якщо всі AI-провайдери недоступні, gmr попереджає і використовує patch bump та сирий git log як release notes - реліз все одно можна створити, але notes варто переглянути. gmr deploy вимагає чистого робочого дерева і запускається з базової гілки (з іншої - попереджає і запитує підтвердження, якщо є TTY).
gmr status
Показує статус останніх CI/CD запусків (GitHub Actions / GitLab Pipelines) для поточної гілки та останнього тега (або явно вказаного ref).
gmr status # поточна гілка + останній тег
gmr status my-branch # конкретна гілка/тег
gmr status --limit 5 # показати 5 останніх запусків на ref (1-20, за замовчуванням 3)
Для кожного ref виводить список запусків (✓/✗/●/○/–) з job'ами найновішого запуску та підсумковий рядок (all pipelines passed / FAILED (<jobs>) / still running / no pipelines found). Завершується кодом 1, якщо найновіший запуск будь-якого перевіреного ref провалився - зручно для скриптів (gmr status || echo "deploy failed").
Configuration
| Змінна |
Опис |
Default |
GEMINI_API_KEY |
API ключ Google Gemini |
- |
ANTHROPIC_API_KEY |
API ключ Anthropic Claude |
- |
OPENAI_API_KEY |
API ключ OpenAI |
- |
GEMINI_MODEL |
Модель Gemini |
gemini-flash-latest |
ANTHROPIC_MODEL |
Модель Claude |
claude-sonnet-4-20250514 |
OPENAI_MODEL |
Модель OpenAI |
gpt-4o-mini |
GEMINI_BASE_URL |
Base URL для Gemini API override |
https://generativelanguage.googleapis.com/v1beta |
ANTHROPIC_BASE_URL |
Base URL для Claude API override |
https://api.anthropic.com |
OPENAI_BASE_URL |
Base URL для OpenAI-compatible API override (наприклад LiteLLM) |
https://api.openai.com |
GMR_PROVIDERS |
Порядок AI-провайдерів; comma-separated, anthropic = claude |
gemini,claude,openai |
GMR_COMMIT_STYLE |
Стиль commit message: human (звичайне речення) або conventional (type: description) |
human |
GMR_MAIN_BRANCH |
Основна гілка |
auto (origin/HEAD, fallback: main/master) |
GMR_MAX_DIFF |
Макс. рядків diff/log для AI |
500 |
GMR_TAG_PREFIX |
Префікс тега для gmr deploy, коли тегів ще немає ("" - без префікса) |
v |
EDITOR |
Редактор для режиму e(edit) |
vim |
NO_COLOR |
Вимкнути ANSI кольори у виводі |
- |
Конфіденційність
Під час генерації commit message gmr виконує git add -A і надсилає
провайдеру diff (до GMR_MAX_DIFF рядків) разом зі статистикою змін. Під час
gmr deploy провайдер отримує обмежений git log. GMR_MAX_DIFF — лише ліміт,
а не спосіб повністю вимкнути передачу даних. Не запускай AI-функції для змін,
які не можна передавати обраному провайдеру; зроби коміт вручну. Детальніше —
у SECURITY.md.
Development
Гайд з локальної розробки, тестів, лінту і релізного процесу — у
DEVELOPMENT.md. Контрибʼюторам також варто прочитати
CONTRIBUTING.md.
License
MIT