'\" t
.\" Title: git-multi
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.79.1
.\" Date: 09/23/2018
.\" Manual: Git Manual
.\" Source: Git 2.19.0.216.g2d3b1c576.dirty
.\" Language: English
.\"
.TH "GIT\-MULTI" "1" "09/23/2018" "Git 2\&.19\&.0\&.216\&.g2d3b1c" "Git Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
git-multi \- execute the same git command in multiple repositories
.SH "VERSION"
.sp
This is \fBv1\&.0\&.6\fR of \fIgit multi\fR \&... hooray!
.SH "SYNOPSIS"
.sp
There are some options for \fBgit multi\fR itself, in which case it is invoked as follows:
.sp
.nf
\fIgit multi\fR []
.fi
.sp
.sp
To execute the same git command in multiple repositories, the invocation is as follows:
.sp
.nf
\fIgit multi\fR []
.fi
.sp
.SH "DESCRIPTION"
.sp
Convenient way to execute the same git command in a set of related repos, currently the repositories of a GitHub user, incl\&. the organizations they are a member of; the repo list is determined via a GitHub API v3 call, and cached locally \fI(in binary format)\fR for performance and offline usage\&.
.SH "OPTIONS"
.PP
\-\-help
.RS 4
you\(cqre looking at it: show the man page
.RE
.PP
\-\-html
.RS 4
open the HTML version of the man page
.RE
.PP
\-\-check
.RS 4
checks all the required settings and configurations
.RE
.PP
\-\-version
.RS 4
print out this script\(cqs version number
.RE
.PP
\-\-refresh
.RS 4
refresh the list of user & organization repos
.RE
.PP
\-\-json
.RS 4
output repository details to JSON
.RE
.PP
\-\-count
.RS 4
print out the count of repos (per type)
.RE
.PP
\-\-list
.RS 4
print out the names of all repos
.RE
.PP
\-\-archived
.RS 4
print out the names of all repos
.RE
.PP
\-\-forked
.RS 4
print out the names of all repos
.RE
.PP
\-\-private
.RS 4
print out the names of all repos
.RE
.PP
\-\-paths
.RS 4
print out the full path for each repos
.RE
.PP
\-\-spurious
.RS 4
list cloned repos whose remote doesn\(cqt match a github\&.com origin
.RE
.PP
\-\-missing
.RS 4
print out names of repos that haven\(cqt been cloned
.RE
.PP
\-\-stale
.RS 4
list repos that have been deleted on github\&.com
.RE
.PP
\-\-excess
.RS 4
list repos that don\(cqt exist on github\&.com
.RE
.PP
\-\-clone
.RS 4
clones missing repositories into
\fB${HOME}/Workarea\fR
(by default)
.RE
.PP
\-\-query (args)
.RS 4
query GitHub repo metadata for each repository
.RE
.PP
\-\-find
.RS 4
print out the repos for which the Ruby code evaluates to true
.RE
.PP
\-\-eval
.RS 4
execute the given Ruby code in the context of each repo
.RE
.PP
\-\-raw
.RS 4
execute the given shell command inside each git repository
.RE
.SH "EXAMPLES"
.sp
count the number of repos
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-list | wc \-l
.fi
.if n \{\
.RE
.\}
.sp
disk usage of each locally cloned repo
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-paths | xargs \-n 1 du \-hs
.fi
.if n \{\
.RE
.\}
.sp
disk usage using the \fB\-\-raw\fR option
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-raw \*(Aqdu \-hs \&.\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
group and count the repos by GitHub\-determined language
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-query language | cut \-f 2 \-d : | sort | uniq \-c | sort \-n \-r
.fi
.if n \{\
.RE
.\}
.sp
find out the most\-used Ruby versions
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-raw \*(Aq[ \-f \&.ruby\-version ] && cat \&.ruby\-version\*(Aq | cut \-f 2 \-d : | sort | uniq \-c | sort \-n \-r
.fi
.if n \{\
.RE
.\}
.sp
find GitHub repos without a description
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-query description | egrep \*(Aq: *$\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
fetch remote branches for all repos
.sp
.if n \{\
.RS 4
.\}
.nf
git multi fetch \-p
.fi
.if n \{\
.RE
.\}
.sp
print out the local branch for each repo
.sp
.if n \{\
.RS 4
.\}
.nf
git multi rev\-parse \-\-abbrev\-ref=strict HEAD
.fi
.if n \{\
.RE
.\}
.sp
find all repos for which the \fIorigin\fR remote isn\(cqt github\&.com
.sp
.if n \{\
.RS 4
.\}
.nf
git multi config \-\-get remote\&.origin\&.url | fgrep \-v git@github\&.com:
.fi
.if n \{\
.RE
.\}
.sp
a kind of "repository creation" report: count the number of repos created in each quarter
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval "class ::Time; def quarter() (month\&.to_f / 3\&.0)\&.ceil; end; end; puts format(\*(Aq%d\-Q%d\*(Aq, created_at\&.year, created_at\&.quarter)" | sort | uniq \-c
.fi
.if n \{\
.RE
.\}
.sp
for each repo, list all remote branches, sorted by the "age" of the last commit on each branch
.sp
.if n \{\
.RS 4
.\}
.nf
git multi for\-each\-ref \-\-sort="\-authordate" \-\-format="%(refname)%09%(authordate:relative)%09%(authorname)" refs/remotes/origin
.fi
.if n \{\
.RE
.\}
.sp
same as above, but columnize the generated output (NOTE: replace \fI^I\fR with CTRL\-V/CTRL\-I in your terminal)
.sp
.if n \{\
.RS 4
.\}
.nf
git multi for\-each\-ref \-\-sort="\-authordate" \-\-format="%(refname)%09%(authordate:relative)%09%(authorname)" refs/remotes/origin | column \-t \-s "^I"
.fi
.if n \{\
.RE
.\}
.sp
same as above, but refresh the list of remote branches first
.sp
.if n \{\
.RS 4
.\}
.nf
git multi fetch \-p ; git multi for\-each\-ref \-\-sort="\-authordate" \-\-format="%(refname)%09%(authordate:relative)%09%(authorname)" refs/remotes/origin
.fi
.if n \{\
.RE
.\}
.sp
find all Rails projects
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-raw \*(Aq[ \-f Gemfile ] && fgrep \-q \-l rails Gemfile && echo uses Rails\*(Aq | cat
.fi
.if n \{\
.RE
.\}
.sp
find all Mongoid dependencies
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-raw \*(Aq[ \-f Gemfile\&.lock ] && egrep \-i "^ mongoid (\&.*)" Gemfile\&.lock\*(Aq | column \-s: \-t
.fi
.if n \{\
.RE
.\}
.sp
find all projects that have been pushed to in the last week
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-find \*(Aq((Time\&.now \- pushed_at) / 60 / 60 / 24) <= 7\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
print out the number of days since the last push to each repository
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval \*(Aqputs "%d days" % ((Time\&.now \- pushed_at) / 60 / 60 / 24)\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
find all projects that have seen activity this calendar year
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-find \*(Aqpushed_at >= Date\&.civil(Date\&.today\&.year, 1, 1)\&.to_time\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
print out all webhooks
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval \*(Aq(hooks = client\&.hooks(project\&.full_name))\&.any? && begin print project\&.full_name ; print "\et" ; puts hooks\&.map { |hook| ["", hook\&.name, hook\&.config\&.url]\&.join("\et") } ; end\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
print out all deploy keys
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval \*(Aq(keys = client\&.list_deploy_keys(project\&.full_name))\&.any? && begin print project\&.full_name ; print "\et" ; puts keys\&.map(&:title)\&.sort\&.join("\et") ; end\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
find all organization repositories that depend on a given org repo, e\&.g\&. \fIbusiness_rules\fR
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-graph | fgrep business_rules
.fi
.if n \{\
.RE
.\}
.sp
generate a dependency graph of all organization repositories using yuml\&.me
.sp
.if n \{\
.RS 4
.\}
.nf
DEPENDENCIES=$( git multi \-\-graph | ruby \-n \-e \*(Aqparent, children = $_\&.split(": ") ; puts children\&.split(" ")\&.map { |child| "[#{parent}]\->[#{child}]" }\*(Aq | tr \*(Aq\en\*(Aq \*(Aq,\*(Aq ) ; open "http://yuml\&.me/diagram/scruffy/class/${DEPENDENCIES}"
.fi
.if n \{\
.RE
.\}
.sp
generate a dependency graph of all organization repositories using Graphviz
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-graph | ruby \-n \-e \*(Aqparent, children = $_\&.split(": ") ; puts children\&.split(" ")\&.map { |child| "\e"#{parent}\e"\->\e"#{child}\e";" }\*(Aq | awk \*(AqBEGIN { print "digraph {\enrankdir=\e"LR\e";\en" } ; { print ; } END { print "}\en" } ; \*(Aq | dot \-Tpng > /tmp/ghor\&.png ; open \-a Preview /tmp/ghor\&.png
.fi
.if n \{\
.RE
.\}
.SH "QUERY ARGUMENTS"
.sp
The following is a list of valid arguments for the \fBgit multi \-\-query\fR option:
.sp
.if n \{\
.RS 4
.\}
.nf
archive_url archived assignees_url
blobs_url branches_url clone_url
collaborators_url comments_url commits_url
compare_url contents_url contributors_url
created_at default_branch deployments_url
description downloads_url events_url
fork forks forks_count
forks_url full_name git_commits_url
git_refs_url git_tags_url git_url
has_downloads has_issues has_pages
has_projects has_wiki homepage
hooks_url html_url id
issue_comment_url issue_events_url issues_url
keys_url labels_url language
languages_url license merges_url
milestones_url mirror_url name
network_count node_id notifications_url
open_issues open_issues_count organization
owner permissions private
pulls_url pushed_at releases_url
size ssh_url stargazers_count
stargazers_url statuses_url subscribers_count
subscribers_url subscription_url svn_url
tags_url teams_url trees_url
updated_at url watchers
watchers_count
.fi
.if n \{\
.RE
.\}
.SH "JQ INTEGRATION"
.sp
\fBjq\fR is like \fBsed\fR for JSON data\&... all of the above query arguments can be used in conjunction with \fBjq\fR to query, filter, map and transform the GitHub repository attributes stored in the local, binary repository cache; here are some examples:
.sp
.if n \{\
.RS 4
.\}
.nf
# print out each repository\*(Aqs name and its description
git multi \-\-json | jq \*(Aq\&.[] | \&.name + ": " + \&.description\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
.if n \{\
.RS 4
.\}
.nf
# print out the name of all "forked" repositories
git multi \-\-json | jq \*(Aq\&.[] | select(\&.fork == true) | \&.full_name\*(Aq
.fi
.if n \{\
.RE
.\}
.SH "FILES"
.PP
\fB${HOME}/Workarea\fR
.RS 4
root directory where repos will been cloned
.RE
.PP
\fB${HOME}/\&.git/multi/repositories\&.byte\fR
.RS 4
local, binary cache of GitHub repository metadata
.RE
.SH "REFERENCES"
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
homepage for
\fBgit\-multi\fR:
\m[blue]\fBhttps://github\&.com/pvdb/git\-multi\fR\m[]
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
the GitHub API:
\m[blue]\fBhttps://developer\&.github\&.com/v3/\fR\m[]
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
the
\fBjq\fR
command\-line utility:
\m[blue]\fBhttp://stedolan\&.github\&.io/jq/\fR\m[]
.RE