Skip to main content

Mergeprint

See what you actually shipped. PR throughput, backlog, merge time, sizes and period-over-period trends, built locally from the gh CLI. The contribution graph says you were busy; this says what landed.

local-first · private repos supported · one self-contained HTML report

Live demo

Mergeprint demo

One command

uvx mergeprint        # or: pipx run mergeprint

Needs Python 3.9+ and the gh CLI logged in. Nothing else. Bleeding edge: uvx --from git+https://github.com/kirilklein/mergeprint mergeprint.

GitHub account [octodev]:
Days to cover [366]:
Public repositories only (private activity never touches disk) [Y/n]:
Repositories to exclude, comma-separated globs like acme/*:
Timezone (IANA name) [Europe/Berlin]:
Country code for public holidays [DE]:

Enter keeps every default. About five minutes later out/index.html opens in your browser. Then use Settings on the page for days off and events: every number recomputes instantly, nothing is rebuilt. mergeprint --yes skips the questions, --no-open skips the browser.

Prefer a clone? git clone https://github.com/kirilklein/mergeprint && cd mergeprint && ./refresh.sh.

Explore your report

  • Overview: four headline metrics, trailing cadence, monthly activity, repository search, and highlights.
  • Explore: cumulative progress, historical backlog, size vs. wait, merge timing, lines moved, and expandable top-five records.
  • Filters: choose 30 days, 90 days, or the full collected range, or enter your own dates. Repository selection applies to both PRs and issues. The repository table remains an overview so you can switch between repositories.
  • Comparisons: equal-length preceding periods, only when both fit inside the collected data. Counts and business-day rates appear together.
  • Settings: timezone, country holidays, time off, events, and hiding repository names for screenshots. Light, dark, and automatic themes are supported.

Older raw.json files still build, but the backlog chart asks you to collect again because those snapshots did not include open PRs. A saved report is a snapshot, not a live inbox.

Your data stays yours

  • Fetched data (raw.json), your answers (config.local.json) and the built page (out/) are written to the current directory and git-ignored. Nothing personal can end up in a fork by accident.
  • The page is one self-contained HTML file. It makes no requests (fonts aside) and stores your settings in your browser only.
  • Sharing is a separate, explicit step: send the file, or run ./publish.sh, which asks before it pushes to a public gh-pages branch.
  • A shareable version without repository names: --anonymize-repos on the build. Without private repos at all: answer yes to public only and the search itself excludes them, so they never reach disk.

More control

python3 -m mergeprint.collect --account someone-else --end 2026-06-30 --days 180
python3 -m mergeprint.collect --exclude-repo 'acme/*' --exclude-repo octodev/dotfiles
python3 -m mergeprint.build --out out/index.html --anonymize-repos

Persistent defaults live in config.local.json (same keys as mergeprint/config.json):

{"timezone": "Europe/Berlin", "country": "DE", "public_only": true,
 "off_days": ["2026-07-06..2026-07-24"], "events": [["2026-03-25", "conference"]]}

Using a coding agent

The repo is small and documented for agents (AGENTS.md). One-line asks that work well:

  • "Run mergeprint and open the result."
  • "Add my vacation from 6 to 24 July to the config and rebuild."
  • "Add a chart of merges per repository per month."
  • "Build a version without repo names and publish it to gh-pages."

How it works

collect.py uses gh api search/issues for authored PRs overlapping the period, including still-open PRs and PRs closed after the selected end date, and for issues opened in the period. Dense date ranges are split to respect GitHub's search cap; incomplete results stop collection rather than produce a partial report. gh pr list supplies sizes for the busiest repositories. build.py inlines those rows, your defaults and a bundled public-holiday table (250 countries, via the holidays package at generation time) into template.html. All aggregation happens in the page, in plain JavaScript with inline SVG charts.

Conventions:

  • Business days exclude weekends, the chosen country's public holidays and your days off. An unlisted day off inflates the denominator, so the per-business-day rate is a floor.
  • Lines of code count only PRs at or below big_pr (10,000) changed lines. Size coverage is reported explicitly; the threshold does not determine whether code is generated or vendored.
  • Issues are counted by creation date: issues opened by the account.
  • Timestamps are converted to the chosen timezone in the browser, DST-aware.
  • Backlog uses creation and closure timestamps, including PRs closed after the selected window. The final partial week ends at the selected date. Reopen cycles are not reconstructed.
  • Merge timing describes when PRs landed, including merges performed by maintainers. It does not measure the author's working hours or impact.

Development checks

./demo.sh
python3 -m py_compile mergeprint/*.py demo/*.py tools/*.py
python3 -m unittest discover -s tests -v
node --test tests/metrics.cjs
uvx --from . mergeprint --help

Node is needed only for the aggregation tests, not to build or view a report. In a browser, check both views, the date and repository filters, records, settings, and mobile layout.

The demo is a deterministic synthetic year for a fictional octodev; ./demo.sh regenerates it into docs/index.html, the only built page tracked in git.

MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mergeprint-0.1.0.tar.gz (61.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mergeprint-0.1.0-py3-none-any.whl (59.1 kB view details)

Uploaded Python 3

File details

Details for the file mergeprint-0.1.0.tar.gz.

File metadata

  • Download URL: mergeprint-0.1.0.tar.gz
  • Upload date:
  • Size: 61.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mergeprint-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9b09c26105217d2aab19823c48dcc693e1edb5a244257968b894a6f8dac8e678
MD5 884292374cb1a52e85e42b06af52d49a
BLAKE2b-256 b75ad5ef1360c09c33ae3097ee1d5d9233e80bd3757a0e61416d66a55ab5339a

See more details on using hashes here.

Provenance

The following attestation bundles were made for mergeprint-0.1.0.tar.gz:

Publisher: release.yml on kirilklein/mergeprint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mergeprint-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mergeprint-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 59.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mergeprint-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a6f18820c8592d118c92c057f64eba9dadfbd32314ee4b33b77cf393b337891c
MD5 aff383ff95569ed1ef83e27a7b31bad8
BLAKE2b-256 42e2b679a35badeda368f07950248bbdf5001956c16882ddebbadc7538341b85

See more details on using hashes here.

Provenance

The following attestation bundles were made for mergeprint-0.1.0-py3-none-any.whl:

Publisher: release.yml on kirilklein/mergeprint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page