Skip to content
Maksim Gruzdev edited this page Oct 25, 2025 · 77 revisions

Git is hard. Git workflows are complex. Write code, not git commands. Test and deploy in a flash. Learn later.

mr is a CLI tool designed to automate routine git branch flow, such as deploying a feature branch across a complex multi-branch workflow.

MR stands for Merge Request (GitLab) or Pull Request (GitHub). Here MR stands for branch (ex.: a branch with name TASK-42).

mr was written to be as git hosting agnostic as possible. If you're ok with some setup, use glab cli or gh cli instead.

Installation

  • node -v >= 18.12
  • git --version >= 2.23
    • git --version >= 2.11.0 fallback added since mr-git-cli 0.1.0
npm install -g mr-git-cli@latest

Try it without installing:

npx mr-git-cli TASK-42

Usage

Switch or create topic branch

# create branch (press Enter to accept)
$ mr TASK-42
# the default repo branch will be detected 
Create new branch 'TASK-4444' from 'origin/main' [Y/n]?
 > git fetch
 > git switch --guess --merge --create TASK-4444 origin/main
Switched to a new branch 'TASK-4444'

# add 'from' to specify any parent branch
$ mr r43 from origin/release

# switch branch
$ mr release
 > git switch --merge --guess release
$ mr TASK-42 
 > git switch --merge --guess TASK-42

Push and create merge request

If you are using GitLab, you can create a GitLab merge request from the command line using push options

$ mr
> npm test
> git push --set-upstream origin TASK-42:TASK-42 -o merge_request.create
  -o merge_request.title='TASK-42 this is first commit message of branch' -o merge_request.target=main
remote: View merge request for TASK-42:
remote:   https://gitlab.local/jonny64/mr-git-cli/-/merge_requests/1
remote:
To ssh://gitlab.local/jonny64/mr-git-cli.git
 * [new branch]      TASK-42 -> TASK-42

GitLab presence is detected by the 'git remote' URL and push options are added to git push to create a merge request:

  • the first commit message of git log --reverse --pretty=format:%s master..TASK-42 is added as the MR title
  • leading digits of such title are converted to the format TASK-42 to create an external issue tracker link in the merge request title
  • merge request target is set as git config branch.TASK-42.mr-target (was set when mr created the branch)
  • git config branch.TASK-42.description is set to prevent duplicates:
    • next time you use mr, it checks git config branch.TASK-42.description and if it is non-empty, it does nothing to prevent duplicate merge requests.

If the branch is already pushed, GitLab push options do nothing. If there is no merge request, you can create it:

  • either via the UI
  • or delete the branch and re-create it with mr: git push origin --delete TASK-42 && mr.

You can run merge (see below) at once: it will push as the first step

Set up test command

Typical workflow:

  • a developer commits broken tests
  • CI sends you an email about a broken pipeline
  • you should fix it. NOW.

mr applies the shift-left principle:

  • a developer commits broken tests
  • the developer can't push: mr runs tests BEFORE branch push
  • the developer has time to fix it

mr will run this command BEFORE any branch push (topic or integration):

$ git config mr.test 'npm test'   # for code repo
$ git config mr.test 'yamllint .' # for ci repo

You can clear any config value via git config --unset

Merge task to integration branch

$ mr TASK-42 to release
> git push --set-upstream origin TASK-42:TASK-42
> git switch release
> git merge origin/TASK-42
> npm test
> git push --set-upstream 

# merge current git branch to release
$ mr to release

Set up branch cascade merge

Typical workflow:

  • merge to one integration branch (e.g., test)
  • merge to another integration branch (e.g., premain)
  • merge to main
  • everything that goes to main should already be merged to the preceding branches in this exact order.

To configure those dependencies:

$ git config --global branch.main.mr-merge-after test,premain

This will confirm merge to test first, then premain, then main (target branch) last (order matters).

Use either --global or --local setting per repository clone, see git config reference.

mr:

  • checks if the branch was already merged via git log --oneline origin/master..origin/srcBranch
  • produces merge commands for all dependent branches
$ mr hotfix to master
 > git push --set-upstream origin hotfix:hotfix
Everything up-to-date

Ok, here is the plan:
  git switch test
  git merge origin/hotfix
  npm test
  git push --set-upstream 
  git switch master
  git merge origin/hotfix
  npm test
  git push --set-upstream 
  git checkout hotfix

Proceed [Y/n]?

> git switch test
> git merge origin/hotfix
> npm test
> git push --set-upstream 
> git switch master
> git merge origin/hotfix
> npm test
> git push --set-upstream 

Aliasing

$ echo "alias task=mr" >>  ~/.bash_aliases
$ echo "alias deploy=mr" >>  ~/.bash_aliases
$ source ~/.bash_aliases

$ task TASK-42 from production
Create new branch 'TASK-42' from origin/production ? y
> git switch --create --guess TASK-42
# yuck yuck
# git commit
$ deploy
remote: View merge request for TASK-42:
remote:   https://gitlab.local/jonny64/mr-git-cli/-/merge_requests/1
$ deploy TASK-42 to production
> git push --set-upstream origin TASK-42:TASK-42
> git switch production
> git merge origin/TASK-42
> npm test
> git push --set-upstream