Managing multiple repositories using mr

Published on April 2, 2015

tl;dr Using the mr tool, you can issue commands to multiple repositories in parallel. You can pull new changes, do simultaneous commits or just get the last commit log.

My typical day starts with updating a few code repositories. When dealing with a small number of repos, it’s not a problem to do a git pull for each repo. But once you handle more than 4-5 repos, it’s worth automating the process.

Also, at times when you forget to commit some work, it can be handy to run git status on all your repos and see which repos need attention.

I recently came across a tool made by Joey: myrepos (or mr). The tool supports many types of repositories - git, mercurial, cvs, svn, etc.

Installation

1
2
3
4
5
6
7
8
# Debian / Ubuntu (recent versions)
sudo apt-get install mr

# Mac
brew install mr

# Others:
# - Just put the `mr` perl script in your `PATH` and it should work!

Core Usage

The tool looks for a file called .mrconfig in the current directory. This is an ini file which tracks all the repositories using relative paths, for example -

1
2
3
4
# file: ~/.mrconfig

[.homesick/repos/dotfiles]
checkout = git clone 'git@github.com:mitthu/dotfiles.git' 'dotfiles'

To start managing an existing repository (ex. handbook), run -

1
2
# handbook repo is located in ~/handbook
$ mr register handbook

The above updates the .mrconfig to -

1
2
3
4
5
6
7
# file: ~/.mrconfig

[.homesick/repos/dotfiles]
checkout = git clone 'git@github.com:mitthu/dotfiles.git' 'dotfiles'

[handbook]
checkout = git clone 'git@github.com:mitthu/handbook.git' 'handbook'

Action: git checkout

To checkout/clone the repositories in .mrconfig (skips already cloned repositories), run -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ mr checkout
mr checkout: /Users/mitthu/.homesick/repos/dotfiles
Cloning into 'dotfiles'...
remote: Enumerating objects: 1406, done.
remote: Total 1406 (delta 0), reused 0 (delta 0), pack-reused 1406
Receiving objects: 100% (1406/1406), 455.60 KiB | 3.21 MiB/s, done.
Resolving deltas: 100% (823/823), done.

mr checkout: /Users/mitthu/handbook
Cloning into 'handbook'...
remote: Enumerating objects: 32, done.
remote: Total 32 (delta 0), reused 0 (delta 0), pack-reused 32
Receiving objects: 100% (32/32), 4.75 KiB | 4.75 MiB/s, done.
Resolving deltas: 100% (14/14), done.

mr checkout: finished (2 ok)

Action: git pull

Now to update (i.e. do a git pull on) all managed repos, run -

1
2
3
4
5
6
7
8
9
10
11
12
# Update all managed repositories (sequential)
$ mr update
mr update: /Users/mitthu/.homesick/repos/dotfiles
Already up to date.

mr update: /Users/mitthu/handbook
Already up to date.

mr update: finished (2 ok)

# Run `git pull` simultaneously, say on 8 repos at a time
$ mr update -j8

Action: git status

To find out any uncommited/unpushed changes, run -

1
2
3
4
5
6
7
$ mr status
mr status: /home/mitthu/.homesick/repos/dotfiles
8c6f508 (HEAD, master) Add X automation script.

mr status: /home/mitthu/handbook

mr status: finished (2 ok)

My Workflow

I have mutiple .mrconfig’s, but I do make sure not to have too many configs. Using too many configs, would defeat the very purpose of automation, as you will be doing mr update in multiple directories.

I have a directory called repos in my home directory -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Directory structure of ~/repos
~/repos
|-- following
|   |-- ansible-django
|   .... (all repos I follow) ....
|   `-- .mrconfig
|-- ops
|   |-- dotfiles_deploy
|   .... (my personal automation stuff) ....
|   `-- .mrconfig
`-- work
    |-- gitosis-admin
    .... (work related stuff) ...
    `-- .mrconfig

Apart from the above, I also have an .mrconfig in my home directory. In this config, I add all the repos which I use daily and all my current projects. So everyday, I just have to do an mr update in my home folder.

There are a few repos to which I do not have push permissions (~/repos/following/*). mr will fail to push to such repos. It does not look very encouraging to see commands failing. To fix this, I override the mr push command for those specific repos. So for example, if I don’t want to push to the handbook repo, I will update my .mrconfig as follows -

1
2
3
4
5
# file: ~/.mrconfig

[handbook]
checkout = git clone 'git@github.com:mitthu/handbook.git' 'handbook'
push = echo "DO NOT PUSH"

The above would just run echo out “DO NOT PUSH” on an mr push run. For example -

1
2
3
4
5
$ mr push
mr push: /home/mitthu/handbook
DO NOT PUSH

mr push: finished (1 ok)

Tips

  • Teach new commands to mr (or overrride existing commands) via the ‘DEFAULT’ section in .mrconfig, for example -
    1
    2
    3
    4
    5
    
    [DEFAULT]
    # Teach mr how to "mr gc" in git repos.
    git_gc = git gc "$@"
    # Always fetch and rebase changes
    git_update = git pull --rebase
    
  • Use cron and mr to keep forked repositories up to date with upstream.
  • Use cron, mr and pushover to send notifications about uncommitted/unpushed changes at 5:00 pm.
  • Doc: man page of mr (man mr)
  • Doc: Homepage - mr

Last updated on: June 18, 2019


← Back to homepage