gr
Features
-
Tag all the things!
gr @work foo
will run the commandfoo
in all the paths tagged@work
. - Auto-discovery of git repositories for easy setup and tag management.
-
gr
does not reinvent any git operations: instead, it passes through and runs any unknown commands. All your git-fu will still work! e.g.gr @work git fetch
is the same as runninggit fetch
in each all the paths tagged@work
. -
Built-in commands for common pain points:
-
status
for one-line summaries of repos (modified, behind/ahead, tags)
-
-
Extensible via plugins and middleware: REST API/Connect-style request handlers (
function(req, res, next) { ... }
Changelog
- Looking for more committers! Let me know if you’re interested; gr currently meets my fairly limited needs but I know it can become even more useful given a larger core team and/or a stronger vision around how it can support usage in a team working on multiple repos.
-
0.5.5
: Submodule directory detection fixes, thanks @bimlas! Better behavior when the EDITOR environment variable is not set, thanks @petercoulton! -
0.5.4
: Addedgr export
andgr import
for sharing.grconfig
, thanks @timja-kainos! Added current branch to status, thanks @n1ywb! -
0.5.3
: minor improvements to internals, thanks @timja-kainos! -
0.5.2
:gr
now handles git submodules better, thanks @cojomojo!gr
also prints out tags with newlines which makes for a more readable output, thanks @nwinkler! -
0.5.1
:gr discover
now handles paths outside the home directory as expected, thanks @farmerchris! -
0.5.0
:gr discover
is now an alias forgr tag discover
;gr discover
now accepts path arguments, shows progress during a scan and only scans five levels deep by default. Thanks @coderjoe for the patches! -
0.4.1
:gr status
now only invokes git once per directory, thanks @coderjoe! -
0.4.0
: Added several usability improvements and bug fixes, courtesy of @nichtich (better handling of missing directories, support for simple paths). Added a fix that improves errors related to directory permissions, courtesy of @pnxs. -
0.3.0
: Switched from#foo
to@foo
for tags; while the#foo
syntax looks cool, most shells will treat it as a comment unless the tag is surrounded by quotes. Looking back at the design, I’d rather go for usability over pretty looking commands. Updated the documentation to match this change.
Example
gr
works by tagging directories with tags:
gr +@work ~/mnt/gluejs ~/mnt/microee
After this, you can run any commands on the tagged directories simply by prefixing them with gr @work
. For example:
gr @work status
Outputs (actual output is colorized):
~/mnt/gluejs 2 modified [ahead 2] @work
~/mnt/microee Clean @work
E.g. path, modified, ahead/behind remote, tags. Alternatively, you can use plain git commands for a more verbose report:
gr @work git status -sb
Outputs:
in ~/mnt/gluejs
## glue2
M lib/runner/package-commonjs/index.js
M index.js
in ~/mnt/microee
## master
gr
doesn’t do any command rewriting, or introduce any new commands – I like git
as it is.
Getting started
First, install Node.js. Node.js adds the node
and the npm
command. On Ubuntu you need the nodejs-legacy
package.
Next, to install gr
(the name was already taken on npm):
npm install -g git-run
You may need to prefix this with sudo
.
Setting up tags
Use the auto-discovery feature to set up tags quickly:
gr tag discover
By default auto-discovery searches all paths under your home directory. gr discover
is also an alias for gr tag discover
(since v0.5.0).
Note that discover only scans up to five levels deep by default (since v0.5.0); if you need to scan even deeper in your path tree you should just pass in an explicit set of starting points to gr tag discover
.
If you’d prefer, you can specify a directory under which auto-discovery should search (since in v0.5.0):
gr tag discover /mnt/external/projects
Once auto-discovery completes, it will generate a list, and open it in your default console editor.
It will look like this:
# Found the following directories with `.git` directories.
#
# Please add any tags you want by adding one or more `@tag`
# entries after the path name.
# For example:
# ~/foo @work @play
# will tag ~/foo with @work and @play.
#
~/foo
~/bar/baz
Add tags after each path, save the file, and exit.
Your tags are now set up!
Verify with gr status
or gr tag list
. Use gr @work status
or gr @work ls -lah
to see how commands are executed. (status
is a built-in command; ls -lah
is not, so it is run in each of the paths.)
You can run auto-discovery multiple times. It makes tag-related bulk changes quite easy.
Tab completion
To add tab completion:
-
open your
~/.zshrc
or~/.bashrc
(~/.bash_profile
on OS X) -
add the line
. <(gr completion)
at the end of the file -
then, to apply this change to your current session, run
source ~/.zshrc
(orsource ~/.bashrc
orsource ~/bash_profile
)
Now, when you type gr <tab>
, you’ll see the list tags you’ve created. If you notice any bugs, let me know via an issue.
How I use gr
Some examples:
COMMAND
TASK
gr @work git fetch
and then gr @work status
Update all my work repos. This fetches the newest information from the remote, and then prints a one-line-at-a-time summary.
gr @work git diff
or gr @work git diff --cached
See diffs
gr @work jshint . --exclude=**/node_modules
Run jshint
gr @write make
Rebuild all my writing via make
gr @work npm ls
List install npm modules
gr @work git --no-pager log --decorate --graph --oneline -n 3
Print a graph-like log
Of course, I don’t actually type these out; I’m using zsh
aliases instead. grd
is for diff, grdc
is for diff --cached
; grl
is for the log. For example, in .zshrc
:
alias grs="gr status"
alias grl="gr git --no-pager log --decorate --graph --oneline -n 3"
You can set up similar aliases for bash
; Google is your friend here.
Usage
Usage:
gr <options> <targets> <cmd>
Options
Currently, there is just one option: --json
, which switched to a machine-readable output and is used for integration tests.
Targets
Targets can be paths or tags. For example:
gr ~/foo ~/bar status
gr @work ls -lah
- Path targets should be directories.
-
Tags refer to sets of directories. They managed using the
tag
built-in.
If no targets are given, then all tagged paths are used. For example, gr status
will report the status of all repositories.
Tagging
Short form:
@tag List directories associated with "tag"
@tag <cmd> Run a command in the directories associated with "tag"
-t <tag> <cmd> Run a command in the directories associated with "tag"
+@tag Add a tag to the current directory
-@tag Remove a tag from the current directory
+@tag <path> Add a tag to <path>
-@tag <path> Remove a tag from <path>
Long form:
tag add <tag> Alternative to +@tag
tag rm <tag> Alternative to -@tag
tag add <t> <p> Alternative to +@tag <path>
tag rm <t> <p> Alternative to -@tag <path>
tag list List all tags (default action)
tag discover Auto-discover git paths under ~/
Example:
gr +@work ~/bar
Internally, the tags are stored in the config file ~/.grconfig.json
. For example, the tag @books
for the path /home/m/mnt/css-book
would be stored as:
{
"tags": {
"books": [
"/home/m/mnt/css-book"
]
}
For some use cases, it may be easier to just edit this file rather than use the commands tag add
and tag rm
.
Commands
The command can be either one of the built-in commands, or a shell command. For example:
gr @work status
gr ~/foo ~/bar ls -lah
To explicitly set the command, use --
:
gr ~/foo -- ~/bar.sh
gr @work -- git remote -v
Tags can also be specified more explicitly. For example gr -t work -t play
is the same as gr @work @play
.
Built-in commands:
gr tag ..
add <t> Add a tag to the current directory
rm <t> Remove a tag from the current directory
add <t> <path> Add a tag to <path>
rm <t> <path> Remove a tag from <path>
gr tag discover <paths> Auto-discover git paths under the list of <paths>
(If omitted, <paths> defaults to ~/)
gr tag list List all known repositories and their tags
gr list List all known repositories and their tags
gr status Displays the (git) status of the selected directories.
gr status -v Runs "git status -sb" for a more verbose status.
gr config ..
get <k> Get a config key (can also be a path, e.g. "tags.foo")
set <k> <v> Set a config key (overwrites existing value)
add <k> <v> Add a value to a config key (appends rather than overwriting)
rm <k> <v> Remove a value from a config key (if it exists)
list List all configuration (default action)
gr help Show this help
gr version Version info
Plugins
TODO:
-
bootstrap
: bootstraps a set of repositories from a config file.
Installing plugins
Generally speaking, you need to do two things:
-
install the plugin globally via npm:
npm install -g foo
-
configure
gr
to use the plugin:gr config add plugins foo
The new commands should now be available.
Writing plugins
Plugins are functions which are invoked once for each repository path specified by the user. This makes it easier to write plugins, since they do not need to handle executing against multiple repository paths explicitly.
Plugins are treated a bit like a REST API: they are defined as “routes” on the gr
object.
Each plugin consists of an index file which is loaded when gr is started, and which should add new “routes”:
module.exports = function(gr) {
// set up new commands on the gr object
gr.use(['foo', 'help'], function(req, res, next) {
console.log('Hello world');
req.exit(); // stop processing
});
gr.use('foo', function(req, res, next) {
console.log(req.argv, req.path);
req.done(); // can be called multiple times
});
};
Of course, req
and res
in the handlers are not HTTP requests, but rather objects representing the target directory (a regular object) and process.stdout
.
Each “route” is called multiple times,…