NEW: Have you already tried the shared team timer timer.mob.sh?
Smooth git handover for remote pair/mob programming.
- mob is an open source command line tool written in go
- mob is the fastest way to hand over code via git
- mob keeps your branches clean and only creates WIP commits on temporary branches
- mob has a shared team timer timer.mob.sh
- mob is on 'assess' in the latest Thoughtworks Technology Radar
Sometimes you come across a tool that you didn't realize you needed until you do; mob is just such a tool. Living as we do in a world where remote pair programming has become the norm for many teams, having a tool that allows for seamless handover either between pairs or a wider group as part of a mob programming session is super useful. mob hides all the version control paraphernalia behind a command-line interface that makes participating in mob programming sessions simpler. It also provides specific advice around how to participate remotely, for example, to "steal the screenshare" in Zoom rather than ending a screenshare, ensuring the video layout doesn't change for participants. A useful tool and thoughtful advice, what's not to like? β Technology Radar Volumne 25, thoughtworks
"Mob has allowed us to run fast-paced, engaging, and effective sessions by enabling sub-10-second handover times and otherwise getting out of the way. A simple but great tool!" β Jeff Langr, developer
"I love it, it is a quantum leap in our collaboration." β Vasiliy Sivovolov, Senior Software Engineer
"What a great tool to organise remote working." β Jennifer Gommans, IT Consultant
"I was recently introduced to mob.sh for remote pairing/mobbing collaboration and I absolutely love it. The timer feature is really a selling point for me. Kudos" β Fabien Illert, IT Consultant
The recommended way to install mob is as a binary via the provided install script:
# works for macOS, linux, and even on windows in git bash
curl -sL install.mob.sh | sh
On macOS via homebrew:
brew install remotemobprogramming/brew/mob
# upgrade to latest version
brew upgrade remotemobprogramming/brew/mob
On Windows via Scoop:
scoop install mob
On Nix through the mob.nix expression:
mob = callPackage ./mob.nix {withSpeech = true;};
On Arch Linux via yay:
yay -S mobsh-bin
On Ubuntu there's an EXPERIMENTAL snap package with a known limitation (ssh-agent not working):
sudo snap install mob-sh
sudo snap connect mob-sh:ssh-keys
When you already have a working go environment with a defined GOPATH you can install latest via go install
:
With go < 1.16
go get github.com/remotemobprogramming/mob
go install github.com/remotemobprogramming/mob
go 1.16 introduced support for package@version syntax, so you can install directly with:
go install github.com/remotemobprogramming/mob@latest
or pick a specific version:
go install github.com/remotemobprogramming/[email protected]
You only need three commands: mob start
, mob next
, and mob done
.
Switch to a separate branch with mob start
and handover to the next person with mob next
.
Repeat.
When you're done, get your changes into the staging area of the main
branch with mob done
and commit them.
Here's a short example on how the two developers Carola and Maria code a feature together and push it in the end.
# Carola
main $ mob start
mob/main $ echo "hello" > work.txt
mob/main $ mob next
# Maria
main $ mob start
mob/main $ cat work.txt # shows "hello"
mob/main $ echo " world" >> work.txt
mob/main $ mob next
# Carola
mob/main $ mob start
mob/main $ cat work.txt # shows "hello world"
mob/main $ echo "!" >> work.txt
mob/main $ mob done
main $ git commit -m "create greeting file"
main $ git push
And here's the man page of the tool:
mob enables a fast Git handover
Basic Commands:
start start mob session from base branch in wip branch
next handover changes in wip branch to next person
done squashes all changes in wip branch to index in base branch
reset removes local and remote wip branch
Basic Commands(Options):
start [<minutes>] Start a <minutes> timer
[--include-uncommitted-changes|-i] Move uncommitted changes to wip branch
[--branch|-b <branch-postfix>] Set wip branch to 'mob/<base-branch>-<branch-postfix>'
next
[--stay|-s] Stay on wip branch (default)
[--return-to-base-branch|-r] Return to base branch
[--message|-m <commit-message>] Override commit message
done
[--no-squash] Do not squash commits from wip branch
[--squash] Squash commits from wip branch
reset
[--branch|-b <branch-postfix>] Set wip branch to 'mob/<base-branch>-<branch-postfix>'
Experimental Commands:
squash-wip Combines wip commits in wip branch with subsequent manual commits to leave only manual commits.
! Works only if all wip commits have the same wip commit message !
[--git-editor] Not intended for manual use. Used as a non-interactive editor (GIT_EDITOR) for git.
[--git-sequence-editor] Not intended for manual use. Used as a non-interactive sequence editor (GIT_SEQUENCE_EDITOR) for git.
Timer Commands:
timer <minutes> start a <minutes> timer
start <minutes> start mob session in wip branch and a <minutes> timer
break <minutes> start a <minutes> break timer
Get more information:
status show the status of the current mob session
config show all configuration options
version show the version of mob
help show help
Other
moo moo!
Add --debug to any option to enable verbose logging
Examples:
# start 10 min session in wip branch 'mob-session'
mob start 10
# start session in wip branch 'mob/<base-branch>/green'
mob start --branch green
# handover code and return to base branch
mob next --return-to-base-branch
# squashes all commits and puts changes in index of base branch
mob done
# make a sound check
mob moo
- Say out loud
- Whenever you key in
mob next
at the end of your turn ormob start
at the beginning of your turn say the command out loud. - Why? Everybody sees and also hears whose turn is ending and whose turn has started. But even more important, the person whose turn is about to start needs to know when the previous person entered
mob next
so they get the latest commit via theirmob start
.
- Whenever you key in
- Steal the screenshare
- After your turn, don't disable the screenshare. Let the next person steal the screenshare. (Requires a setting in Zoom)
- Why? This provides more calm (and less diversion) for the rest of the mob as the video conference layout doesn't change, allowing the rest of the mob to keep discussing the problem and finding the best solution, even during a Git handover.
- Share audio
- Share your audio when you share your screen.
- Why? Sharing audio means everybody will hear when the timer is up. So everybody will help you to rotate, even if you have missed it coincidentally or deliberately.
- Use a timer
- Always specify a timer when using
mob start
(for a 5 minute timer usemob start 5
) - Why? Rotation is key to good pair and mob programming. Just build the habit right from the start. Try to set a timer so everybody can have a turn at least once every 30 minutes.
- Always specify a timer when using
- Set up a global shortcut for screensharing
- Set up a global keyboard shortcut to start sharing your screen. In Zoom, you can do this via Zoom > Preferences > Keyboard Shortcuts. More tips on setting up Zoom for effective screen sharing.
- Why? This is just much faster than using the mouse.
- Set your editor to autosave
- Have your editor save your files on every keystroke automatically. IntelliJ products do this automatically. VS Code, however, needs to be configured via "File > Auto Save toggle".
- Why? Sometimes people forget to save their files. With autosave, any change will be handed over via
mob next
.
mob-start feature1
creates a new base branch feature1
to immediately start a wip branch mob/feature1
from there.
mob-start() { git checkout -b "$@" && git push origin "$@" --set-upstream && mob start --include-uncommitted-changes; }
alias ms='mob start'
alias mn='mob next'
alias md='mob done'
alias moo='mob moo'
mob version
#v1.11.0
alias ensemble='mob' # introduce alias
export MOB_CLI_NAME='ensemble' # makes it aware of the alias
ensemble next
#π to start working together, use
#
# ensemble start
#
And starting with v1.12.0, mob
is symlink aware as well:
mob version
#v1.12.0
ln -s /usr/local/bin/mob /usr/local/bin/ensemble
ensemble next
#π to start working together, use
#
# ensemble start
#
Say you're a larger team and work on the same git repository using ticket numbers as branch modifiers. It's easy to forget exporting the room that enables the integration with timer.mob.sh. Here's a function that helps to do this:
# Automatically set the room based on the branch modifier
#
# example
#
# $ ms team-123 15
ms() {
BRANCH=$1
MINUTES=$2
export MOB_TIMER_ROOM=$BRANCH
mob start --branch $BRANCH $MINUTES
}
(This is not needed when installing via snap.)
To get the timer to play "mob next" on your speakers when your time is up, you'll need an installed speech engine. Install that on Debian/Ubuntu/Mint as follows:
sudo apt-get install espeak-ng-espeak mbrola-us1
or on Arch Linux as follows:
sudo pacman -S espeak-ng-espeak
yay -S mbrola-voices-us1
Create a little script in your $PATH
called say
with the following content:
#!/bin/sh
espeak -v us-mbrola-1 "$@"
If you use WSL2 on windows, install eSpeak as windows tool and Create a little script in your $PATH
called say
with the following content:
#!/bin/sh
/mnt/c/Program\ Files\ \(x86\)/eSpeak/command_line/espeak.exe "$@"
make sure that the path to the windows espeak.exe
fits your installation.
You can avoid the long path by adding it to your windows path variable.
Show your current configuration with mob config
:
MOB_CLI_NAME=mob
MOB_REMOTE_NAME=origin
MOB_WIP_COMMIT_MESSAGE=mob next [ci-skip] [ci skip] [skip ci]
MOB_REQUIRE_COMMIT_MESSAGE=false
MOB_VOICE_COMMAND=say "%s"
MOB_VOICE_MESSAGE=mob next
MOB_NOTIFY_COMMAND=/usr/bin/osascript -e 'display notification "%s"'
MOB_NOTIFY_MESSAGE=mob next
MOB_NEXT_STAY=true
MOB_START_INCLUDE_UNCOMMITTED_CHANGES=false
MOB_WIP_BRANCH_QUALIFIER=
MOB_WIP_BRANCH_QUALIFIER_SEPARATOR=-
MOB_DONE_SQUASH=true
MOB_TIMER=
MOB_TIMER_ROOM=
MOB_TIMER_ROOM_USE_WIP_BRANCH_QUALIFIER=false
MOB_TIMER_LOCAL=true
MOB_TIMER_USER=
MOB_TIMER_URL=https://timer.mob.sh/
MOB_STASH_NAME=mob-stash-name
Override default value permanently via environment variables:
export MOB_NEXT_STAY=true
Override default value just for a single call:
MOB_NEXT_STAY=true mob next
Mob can simply be uninstalled by removing the installed binary (at least if it was installed via the http://install.mob.sh script).
rm /usr/local/bin/mob
rm ~/bin/mob.exe
brew uninstall remotemobprogramming/brew/mob
Propose your change in an issue or directly create a pull request with your improvements.
# PROJECT_ROOT is the root of the project/repository
cd $PROJECT_ROOT
git version # >= 2.17
go version # >= 1.15
go build # builds 'mob'
go test # runs all tests
go test -run TestDetermineBranches # runs the single test named 'TestDetermineBranches'
# run tests and show test coverage in browser
go test -coverprofile=cover.out && go tool cover -html=cover.out
- mob is a thin wrapper around git.
- mob is not interactive.
- mob owns its wip branches. It will create wip branches, make commits, push them, but also delete them.
- mob requires the user to do changes in non-wip branches.
- mob provides a copy'n'paste solution if it encounters an error.
- mob relies on information accessible via git.
- mob provides only a few environment variables for configuration.
- mob only uses the Go standard library and no 3rd party plugins.
- INNOQ
- BLUME2000
- REWE Digital
- And probably many others who shall not be named.
Developed and maintained by Dr. Simon Harrer.
Contributions and testing by Jochen Christ, Martin Huber, Franziska Dessart, Nikolas Hermann and Christoph Welcz. Thank you!
Logo designed by Sonja Scheungrab.
<style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>