'\" t
.\"     Title: git-multi
.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\"      Date: 01/06/2020
.\"    Manual: Git Manual
.\"    Source: Git 2.23.0.606.g08da6496b6.dirty
.\"  Language: English
.\"
.TH "GIT\-MULTI" "1" "01/06/2020" "Git 2\&.23\&.0\&.606\&.g08da64" "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 \fBv2\&.9\&.0\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 \-\-<option> [<option_arguments>]
.fi
.sp
.sp
To execute the same git command in multiple repositories, the invocation is as follows:
.sp
.nf
\fIgit multi\fR <git_command> [<git_command_arguments>]
.fi
.sp
.sp
Both ways of running \fBgit multi\fR take an optional, so\-called "multi repo" argument to limit the operation to the list of repositories in the referenced "multi repo", ie\&. a single GitHub user or a single GitHub organization:
.sp
.nf
\fIgit multi\fR ++<multi_repo> \-\-<option> [<option_arguments>]
.fi
.sp
.sp
and\&...
.sp
.nf
\fIgit multi\fR ++<multi_repo> <git_command> [<git_command_arguments>]
.fi
.sp
.SH "DESCRIPTION"
.sp
Convenient way to execute the same git command in a set of related repositories, which could be all GitHub repos for a given user, or all repos for a given GitHub organization\&.
.sp
Multipe users and organizations can be configured, and by default \fBgit multi\fR operates on all repositories for all users and all orgs; to limit the operation to a single user or a single org, the optional \fB++<multi_repo>\fR argument can be used\&.
.sp
The list of repos for a user or an org is determined via a GitHub API v3 call and cached locally \fI(in binary format)\fR for performance and offline usage\&.
.SH "OPTIONS"
.PP
\-\-version
.RS 4
print out this script\(cqs version number
.RE
.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
\-\-report
.RS 4
report on various repository stats and metrics
.RE
.PP
\-\-count
.RS 4
print out the count of repos (per type)
.RE
.PP
\-\-refresh
.RS 4
refresh the list of user & organization repos
.RE
.PP
\-\-json
.RS 4
output repository details to JSON
.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
\-\-missing
.RS 4
print out names of repos that haven\(cqt been cloned
.RE
.PP
\-\-clone
.RS 4
clones missing repositories into
\fB${HOME}/Workarea\fR
(by default)
.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
\-\-spurious
.RS 4
list cloned repos whose remote doesn\(cqt match a github\&.com origin
.RE
.PP
\-\-query (args)
.RS 4
query GitHub repo metadata for each repository
.RE
.PP
\-\-find <ruby>
.RS 4
print out the repos for which the Ruby code evaluates to true
.RE
.PP
\-\-eval <ruby>
.RS 4
execute the given Ruby code in the context of each repo
.RE
.PP
\-\-raw <cmd>
.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 (using \fBsymbolic\-ref\fR)
.sp
.if n \{\
.RS 4
.\}
.nf
git multi symbolic\-ref \-\-quiet \-\-short HEAD
.fi
.if n \{\
.RE
.\}
.sp
print out the local branch for each repo (using \fBrev\-parse\fR)
.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\&.utc \- 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 "%s \- %d days" % [full_name, ((Time\&.now\&.utc \- pushed_at) / 60 / 60 / 24)\&.ceil]\*(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\&.utc\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
print out all webhooks
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval \*(Aqputs client\&.hooks(full_name)\&.map { |hook| [full_name, hook\&.name, hook\&.config\&.url]\&.join("\et") }\*(Aq
.fi
.if n \{\
.RE
.\}
.sp
delete all webhooks with matching URLs (in this case: \fBnotify\&.travis\-ci\&.org\fR)
.sp
.if n \{\
.RS 4
.\}
.nf
git multi \-\-eval \*(Aqclient\&.hooks(full_name)\&.find_all { |hook| hook\&.config\&.url =~ /notify\&.travis\-ci\&.org/ }\&.each { |hook| client\&.remove_hook(full_name, hook\&.id) }\*(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(full_name))\&.any? && begin print 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          disabled             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
temp_clone_token     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 \-r \*(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 \-r \*(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
.PP
\fB${HOME}/\&.git/multi/superprojects\&.config\fR
.RS 4
definitions for so\-called "superproject" multi repos
.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