A GitHub CLI extension that surfaces the public email addresses behind GitHub commits. It runs in one of two modes depending on the argument shape:
- User mode — given a username or profile URL, list every email the user
has authored commits with across their public repositories, with the
earliest and latest commit dates per email (
YYYYMMDD, UTC). - Repo mode — given an
owner/repo(or repo URL), list every contributor to that repository with the distinct emails they have authored commits with.
Output is JSON on stdout, so it pipes cleanly into jq and friends.
gh extension install th2ch-g/gh-email-getTo upgrade later:
gh extension upgrade email-getgh email-get [flags] <username-or-url|owner/repo>Accepted argument shapes:
| Shape | Mode |
|---|---|
th2ch-g / github.com/th2ch-g / https://github.com/th2ch-g |
User mode |
th2ch-g/dotfiles / github.com/th2ch-g/dotfiles / https://github.com/th2ch-g/dotfiles(.git) |
Repo mode |
Trailing slashes and an optional .git suffix on repo URLs are stripped.
$ gh email-get th2ch-g
[
{
"email": "76892059+th2ch-g@users.noreply.github.com",
"start_date": "20221005",
"last_date": "20260424"
}
]Pipe into jq to filter — e.g. drop noreply addresses:
gh email-get th2ch-g --quiet | jq '[.[] | select(.email | contains("noreply") | not)]'$ gh email-get th2ch-g/dotfiles
[
{
"user": "th2ch-g",
"emails": [
"76892059+th2ch-g@users.noreply.github.com"
]
}
]Drop bots / noreply contributors:
gh email-get owner/repo --quiet \
| jq '[.[] | select(.user | endswith("[bot]") | not)]'| Flag | Default | Mode | Description |
|---|---|---|---|
--quiet |
false |
both | Suppress progress messages on stderr. |
--include-fork |
true |
user only | Include forked repositories owned by the user. Pass --include-fork=false to skip them. |
--contributions |
false |
user only | Also scan external repositories the user has authored commits in, discovered via the GitHub search/commits API. The search endpoint has a tighter rate limit (30 req/min when authenticated) and returns at most 1000 results. |
--include-fork and --contributions only affect user mode; in repo mode the
single specified repository is always scanned exactly once.
--help prints the usage and exits.
The argument is parsed into a Target{Kind, User, Repo} value. Two-segment
inputs (owner/repo) become repo-mode targets; one-segment inputs become
user-mode targets. Anything with three or more path segments is rejected.
GET /users/{user}/repos?type=ownerlists the user's owned public repos (paginated via theLinkheader). Forks are included unless--include-fork=false.- With
--contributions,GET /search/commits?q=author:{user}discovers external repos the user has committed to. Results are de-duplicated against the owned set. - For each repo,
GET /repos/{owner}/{repo}/commits?author={user}paginates through every commit authored by the user. Empty repositories return HTTP 409 from the API and are silently treated as having zero commits. - Each commit's
commit.author.emailandcommit.author.dateis fed into an aggregator that keeps the earliest and latest date per email. - The result is sorted by
start_dateascending (email as tie-breaker) and printed as a pretty-printed JSON array.
GET /repos/{owner}/{repo}/commitspaginates through every commit in the repository (no?author=filter).- Each commit is grouped by its author — preferring the GitHub login
(
author.login, top-level on the payload) and falling back to the Git author name (commit.author.name) when the email isn't tied to a GitHub account. - Per user, distinct emails are collected (case-sensitive de-duplication).
- Both users and the emails within each user are sorted alphabetically.
In both modes only commit.author.email is collected; commit.committer.email
is intentionally ignored because rebase/squash flows on GitHub leak GitHub's
own committer identity.
The extension uses the credentials gh is already authenticated with via
go-gh — there is no separate token to
configure. Make sure gh auth status succeeds before running.
The authenticated REST limit is 5000 req/h, enough for users with hundreds
of repos and thousands of commits. The search/commits endpoint used by
--contributions is limited to 30 req/min, so very active users may hit
that ceiling.
- stdout: machine-readable pretty-printed JSON only. Suitable for piping
into
jqor other tools. - stderr: human-readable progress messages, warnings, and errors.
A commit with an empty author email or zero-value date is skipped with a warning on stderr; it does not cause the run to fail.
Build and test from a checkout:
make build # produces ./gh-email-get
make test # go test ./...
make install # (re)install as a gh extension from this checkout
make help # list all targetsThe CI workflow at .github/workflows/ci.yml runs go vet, go test, and
go build on every push to main and every pull request.
Release builds are produced by .github/workflows/release.yml using the
cli/gh-extension-precompile
action. Tag the commit and push the tag — the workflow builds per-platform
binaries named gh-email-get_<os>_<arch> and attaches them to a GitHub
Release. gh extension install automatically picks the matching asset.
git tag v0.1.0
git push origin v0.1.0See repository for license details.