Skip to content
jonny64 edited this page Jan 26, 2025 · 74 revisions

Git is hard. Git workflows are complex. Write code, not git commands, deploy it 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 setting up 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 install:

npx mr-git-cli TASK-42

Usage

Switch or create topic branch

# create branch (press Enter to accept)
$ mr TASK-42
# 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 gitlab merge request from 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 'git remote' url and push options are added to git push to create merge request:

  • first commit message of git log --reverse --pretty=format:%s master..TASK-42 is added as MR title
  • leading digits of such title are converted to format TASK-42 to create external issue tracker link in merge request title
  • merge request target is set as git config branch.TASK-42.mr-target (was set when mr created 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 request.

If branch is already pushed, gitlab push option does nothing. You can recreate merge request at same branch: git push origin :TASK-42.

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

Set up test command

Typical workflow:

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

mr applies the shift-left principle:

  • a developer commits broken tests
  • a developer can't push: mr run tests BEFORE branch push
  • a 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

you can use pre and post scripts to group all your checks in one command, ex.:

  • pretest for static analysis scan
  • test to run tests
  • posttest for security scans

now npm test will run all checks in this exact order

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 (ex: test)
  • merge to another integration branch (ex: premain)
  • merge to main
  • everything that goes to main should already be merged to preceding branches in this exact order. To configure those dependencies:
$ git config --global branch.main.mr-merge-after test,premain

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

use either --global or --local setting per repository clone, see git config reference mr

  • checks if branch was already merged via git log --oneline origin/master..origin/srcBranch
  • produce 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