MergeStat: SQL over your git repositories
MergeStat enables anyone to ask questions about the history and contents of source code.
At a glance
- What is it?
- MergeStat is a self-hosted service that syncs git repositories and GitHub data into a Postgres database so you can query your source code with SQL. The local CLI version of the same idea now lives in a separate project called mergestat-lite, which tells you where the pieces have moved.
- Who is it for?
- Adopt MergeStat if you need to ask recurring questions about many repositories at once, such as how churn or contributor activity looks across a fleet of services, since the SQL surface is what makes that possible. Do not adopt it for a single repository, because for that case mergestat-lite is the tool and this service is a whole platform.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The idea: your git history becomes a database
MergeStat enables SQL queries for data in git repositories and related sources such as the GitHub API, and the README states the purpose in plain terms: it allows you to ask questions about the history and contents of your source code.
That framing is the whole concept. A git repository already contains a structured record of who changed what, when, and in which files. MergeStat flattens that record into relational tables and lets you write ordinary SQL over it. Once the data is in Postgres, the questions become joins and aggregates rather than shell pipelines over git log.
The practical difference shows up as soon as you want to compare repositories. How many commits touched a given path across twenty services last quarter is a single GROUP BY once the data is loaded, and it is the kind of question that is awkward to answer consistently with git log plumbing across twenty clones.
The GitHub API is the second source, and the README is explicit that it includes repo auto imports and any sync type that uses the GitHub API, along with private repositories. That means MergeStat is not only about local git history; it can pull in pull requests, issues, workflow runs and repository metadata from GitHub.
The repository layout matches the architecture. There is a cmd/ directory for the binaries, internal/ for the Go internals, pkg/ for the public packages, queries/ for the SQL, migrations/ for the schema changes, graphql/ for the API layer, and a ui/ directory for the frontend. The examples/ directory is split into git/, github/ and templates/, which mirrors the three ways you can use the system.
The primary language is listed as TypeScript, which reflects the ui/ component, and the service itself is Go with a go.mod declaring go 1.19.
mergestat-lite: the CLI was split out
The README carries a note that changes how you should think about this repository, and it sits near the top for that reason.
If you are looking for the CLI, which runs SQL queries against local git repositories, it's now called mergestat-lite and lives in its own repository.
So the project has been split. The local, single-repository, command-line experience is a separate tool, and what remains here is the server-side platform: a sync service that ingests repositories, a Postgres database to hold the result, a GraphQL API and a management UI.
That split matters for anyone picking a tool. If what you want is to run a query against the repository in your current directory, that is a mergestat-lite install and not this one. What this repository gives you is the ability to have many repositories synced, queried together and scheduled.
The go.mod reflects the dependency, with github.com/mergestat/mergestat-lite at v0.5.10 required alongside the other dependencies. The name and version suggest it is a normal library dependency rather than an optional extra, so the two halves are developed together even though they live in different repositories.
The examples directory gives a hint about the three usage modes. examples/git/ covers the local git case, examples/github/ covers the GitHub API case, and examples/templates/ suggests templated or generated queries, which is how a set of standard questions can be applied uniformly across many repositories.
The primary language in the repository metadata is TypeScript, which matches the presence of the ui/ directory and a frontend CI workflow separate from the Go worker CI.
Running it locally with docker-compose
The README gives a single command for local evaluation, which is the right way to try a service with this many moving parts.
docker-compose upOnce it is up, visiting http://localhost:3300/ gives you the management UI where you can begin adding repositories and syncing data.
There is one prerequisite that catches people out, and the README flags it in capitals. If you would like to make use of GitHub API data, any sync type that uses the GitHub API including repo auto imports, or any private GitHub repos, you will need to supply a GitHub personal access token. The README says the team is working on GitHub auth alternatives, but for now a PAT is your best bet. A single PAT can be managed for your instance in the Settings area of the management UI.
The docker-compose file shows the actual service topology. The postgres service is postgres:14 with a db_data volume, a healthcheck running pg_isready, and a password that defaults to password. The worker service is the component that does the syncing, and it runs privileged with a ten minute stop grace period, which is a sensible allowance for a process that may be in the middle of a long git operation. Its healthcheck curls the metrics endpoint on port 8080, which is exposed on the host as 3301.
The worker environment is where most of the tuning lives.
POSTGRES_CONNECTION: postgres://postgres:${POSTGRES_PASSWORD:-password}@postgres:5432/postgres?sslmode=disable
CONCURRENCY: 5
GITHUB_RATE_LIMIT: 1/2
ENCRYPTION_SECRET: ${ENCRYPTION_SECRET:-password}
LOG_LEVEL: debug
GITHUB_WORKFLOW_PER_PAGE: 30CONCURRENCY sets how many syncs run at once, and GITHUB_RATE_LIMIT is expressed as a fraction, which reads as a fraction of GitHub's allowance rather than a raw request count. The per-page settings control how much of the GitHub API's paginated data is pulled in one request.
The compose file also carries commented-out volumes for mounting local repositories, with the instruction that when adding a repo in the UI you use file:///projects/<repo-name> to reach it. That is the path for working against a repository on disk rather than one fetched from a remote.
Resetting a local instance, and the build behind the image
There is a documented procedure for resetting a local instance, which tells you something about how state is stored.
If you would like to start from scratch, that is to reset the DB and start with a fresh deployment, you run the following.
docker-compose downdocker volume rm mergestat_db_dataTwo steps rather than one, and the reason is that the database lives in a named volume rather than in the container filesystem. Tearing down the compose stack does not remove the volume, so the volume has to be removed explicitly. That is also the behaviour that makes a reset a reset rather than a partial one, and it is worth knowing before you go looking for a flag that does not exist.
The Dockerfile shows how much tooling the service image carries, and it is a lot. The build stage is golang:1.19-alpine3.18 with cmake, git, make, gcc, libtool and g++ installed, and a script that installs libgit2 from source rather than using the Alpine package, because the build passes a static,system_libgit2 tag set and needs the shared library on the include path.
The runtime stage is alpine:3.18 and it installs curl, postgresql-client, ca-certificates, git, go, podman, fuse-overlayfs, tini and openssl1.1-compat, then adds a series of scanning binaries by downloading them in the build stage: migrate from the golang-migrate releases page, trivy at v0.31.3 for the TRIVY_REPO_SCAN sync type, syft at v0.58.0, gosec at v2.14.0, grype at v0.56.0, and gitleaks copied from a separate image. The gitleaks and the other scanners are there because the sync types can scan a repository for secrets and vulnerabilities.
The presence of podman and fuse-overlayfs in the runtime image is the least obvious entry and the most telling. It means the worker can run containers itself, which is what makes container-based repository analysis possible inside the sync pipeline.
The Makefile targets follow the same shape, with a worker target that builds with the static,system_libgit2 tags, and dev, ui-dev and docker-build targets that layer the compose files.
Where MergeStat is the wrong choice
There are three limits, and the first is about the gap between the platform and the question.
The first is that MergeStat is a platform, not a query tool, and adopting it means adopting a database, a worker, an API and a UI to answer questions about code. If your question is about one repository, that is the wrong weight of tool, and mergestat-lite is the right one. The split the maintainers made themselves is an admission of this.
The second is operational, and it is visible in the compose file and the Dockerfile. The worker runs privileged. The runtime image carries podman and fuse-overlayfs so the sync pipeline can run containers. The image installs seven separate scanning binaries. A service that clones repositories, runs scanners and executes containers is a service with a large attack surface, and running it privileged inside your infrastructure is a decision with consequences. That is fine on a laptop and worth thinking hard about on a shared cluster.
The third is the version picture, and here the facts line up in a way that deserves attention. The last push was on 2026-10-13 and the repository is not archived, so work is happening. The recent releases are v2.3.2-beta and v2.3.0-beta, both from 2023-11-14, and v2.2.1-beta from 2023-05-30. Every published version is a beta and the newest is nearly three years old.
That matters more than usual here, because the docker-compose file pins the worker image to the tag 2.3.2-beta. So a deployment that follows the documented steps runs the 2023 build, not the current source, even though the source has moved on. If you want the latest behaviour you are building images yourself from the tree, and the Makefile's docker-build targets exist for that.
There is a further wrinkle. The README notes that GitHub API data requires a personal access token and that the team is working on GitHub auth alternatives, which suggests the authentication story is still in flux.
The honest alternative depends on the question. For one repository, git log and a few shell commands, or the mergestat-lite CLI, will answer it without infrastructure. For many repositories and a recurring reporting need, the alternative to MergeStat is not another tool but a different approach: a scheduled export of the fields you care about into your own warehouse, which puts the data under your existing tooling and dashboards but leaves the git-to-tabular transformation to you. MergeStat's value is that it has already solved that transformation and ships the SQL schema with it, which is exactly what makes the platform worth its weight when the question is genuinely cross-repository and recurring.
Editorial conclusion
Adopt MergeStat if you need to ask recurring questions about many repositories at once, such as how churn or contributor activity looks across a fleet of services, since the SQL surface is what makes that possible. Do not adopt it for a single repository, because for that case mergestat-lite is the tool and this service is a whole platform. Before you deploy, note that the last push was on 2026-10-13 while the newest published release is v2.3.2-beta from 2023-11-14, and that the docker-compose file pins worker images to that 2.3.2-beta tag.
Frequently asked questions
What is MergeStat and what does it do?
It enables SQL queries for data in git repositories and related sources such as the GitHub API, so you can ask questions about the history and contents of your source code. Repositories are synced into a Postgres database and served through a GraphQL API and a management UI.
What happened to the MergeStat CLI?
The CLI, which runs SQL queries against local git repositories, is now called mergestat-lite and lives in its own repository at github.com/mergestat/mergestat-lite. The service in this repository handles the synced, multi-repository side.
How do I run MergeStat locally?
Clone the repository and run docker-compose up, then visit http://localhost:3300/ for the management UI where you can add repositories and sync data. To reset a local instance you run docker-compose down followed by docker volume rm mergestat_db_data.
Do I need a GitHub token to use MergeStat?
Yes, for GitHub API data. The README says any sync type that uses the GitHub API, including repo auto imports, and any private GitHub repos, requires a GitHub personal access token. A single PAT can be managed for the instance in the Settings area of the management UI.
Which version of MergeStat does the docker-compose file run?
The worker image is pinned to 2.3.2-beta from images.mergestat.com, and the newest published release is v2.3.2-beta from 2023-11-14. The last push to the repository was on 2026-10-13, so the source has moved on since that image was tagged.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mergestat-mergestat)Was this useful?