Real git. No magic.
A fill is a small git repository whose commits carry the dates you chose, pushed once to a new repo on your account. This page is the whole pipeline, stage by stage, with the parts that make the squares count.
The five stages
Every fill runs the same five stages. The first two happen in your browser while you preview; the last three run on our server and stream back to you as a live log.
- PlanSeeded realism engine in your browser. Same seed, same plan, everywhere.
- ScheduleCommit times inside a timezone-safe window, grouped into sessions.
- BuildA real git repository with real files, built by pure JavaScript.
- PushOne push to a brand-new repo you named, with your OAuth token.
- VerifyGitHub counts the commits by its own rules. We watch the squares land.
1 · Plan: a seeded realism engine
The realism engine is a pure, deterministic function. Give it a seed, a date range and a persona’s parameters and it returns a list of { date, commits } pairs. The same seed produces the same plan in your browser and on our server, which is why the preview you see is exactly what gets pushed.
It models the things that make a real developer’s year look uneven:
- Weekday vs weekend factors. A 9-to-5 dev is quiet on Saturdays; a Weekend Warrior is the reverse.
- An energy curve. Layered sine waves plus a random walk, so there are busy months and quiet months instead of flat noise.
- Streak momentum. Active yesterday means more likely active today, with a small chance for long runs to break.
- Vacations and holidays. Multi-day holes a few times a year, a late-December dip, and optional regional public holidays (US, EU, UK, India, Brazil, China, Japan).
- Heavy-tailed commit counts. A log-normal distribution: mostly 1–4 commits, sometimes 12, rarely a crunch spike.
- “Match my rhythm”. When a real graph is loaded, the engine can infer your own weekday/weekend ratio, streakiness and burstiness from it and continue that pattern instead of a generic persona.
Respect existing is on by default: with a real calendar loaded, days that already have contributions are skipped entirely. Your real commits are never moved, edited or overwritten — they live in other repositories we never touch.
Before anything runs, the server validates the plan again: dates inside the range, at most 30 commits on any day, at most 20,000 commits per fill, a valid repository name. Then it checks entitlement — a free fill covers up to 7 days and 20 commits; beyond that, one credit per 365-day window, or Time Machine for everything.
2 · Timestamps: sessions inside a safe window
A day with n commits becomes one to three work sessions. Session starts are drawn from a mixture — working hours peaking early afternoon, an evening block around 21:00, and a small late-night tail — and commits inside a session are ninety seconds to fifty-five minutes apart, strictly increasing. Weekends lean later.
You confirm your UTC offset in Settings and every commit carries it in the git timestamp, so git log shows the local time you would have been working:
commit 3e1c0d2f9a4b7c6d (HEAD -> main)
Author: octocat <583231+octocat@users.noreply.github.com>
Date: Tue Mar 4 21:37:12 2025 -0500
Add note: rebase --onto explainedThe safe window. GitHub’s reference docs say a commit is filed under the date in its timestamp’s timezone; an older concept page says UTC. Rather than bet, we only pick wall-clock times whose UTC date equals the local date. For UTC−5 that means commits land between 00:00 and 19:00 local; for UTC+5:30, between 05:30 and midnight. Either reading puts the square on the day you painted.
3 · Build: a real repository, built by pure JavaScript
We build the repository with isomorphic-git, a git implementation in plain JavaScript. There is no git binary involved, so the engine runs identically in a container or on a laptop. For every planned commit the engine writes real files, stages them, and commits with your display name and noreply address as both author and committer, using the scheduled timestamp and offset.
The files are not an activity log. You pick one of three templates and the repo grows the way a small real project does:
| Template | What the commits write | Suggested name |
|---|---|---|
| TIL notes | A “Today I Learned” knowledge base: short notes on git, TypeScript, Docker, SQL and more, filed by topic, with a README index that regenerates as notes are added. | til |
| Utility library | A dependency-free TypeScript utils package that grows over time with conventional commits, tests and releases. | ts-utils |
| Dev journal | Short daily engineering notes, one file per day. | dev-journal |
Commit messages are drawn from large, template-specific pools with no repeats in a row. Time Machine lets you supply your own messages, and can spread one plan across up to three repositories.
4 · Push: once, to a repo that did not exist a second ago
At execute time we create the repository through GitHub’s API with the name you typed — public by default, private on Time Machine and Autopilot — and refuse to continue if a repository with that name already exists. Then main is pushed once over smart HTTP, authenticated with your OAuth token exactly the way git push would be. The log you watch is the real progress of that build and push.
Permissions are asked for as late as possible and kept as small as possible:
| When | Scopes | Why |
|---|---|---|
| Sign in | read:user user:email | Know who you are and load your calendar. Read-only. |
| Execute (public or private repo) | contents, administration on the repositories you pick | Push the commits, set the default branch, and delete that repository again on undo. GitHub refuses anything outside the list. |
Sign-in is an OAuth app and stays read-only. Everything that writes is a GitHub App you install on the repositories you choose, so the limit is GitHub's, not a promise of ours: a write anywhere else is refused, and a private repository you did not pick is invisible. Change or remove it at github.com/settings/installations, and revoke sign-in at github.com/settings/applications. Keys last one hour, are narrowed to a single repository, are encrypted at rest with AES-256-GCM and are never written to logs.
5 · Verify: why the squares count
GitHub counts a commit towards your graph when four things are true. The pipeline is built so all four hold by construction:
- The author email is linked to your account. We use
<id>+<login>@users.noreply.github.com, which every GitHub account owns automatically — no verification dance, no exposure of your real address. - The commit is on the default branch.
mainis the first and only branch of the new repo, so it is the default. - The repository is not a fork. It is created empty by the API.
- You can see the repository. Public repositories count everywhere. Private ones count when “Include private contributions” is enabled on your profile — the Doctor reminds you.
GitHub usually redraws within minutes and documents up to 24 hours. After a push, the app keeps checking your calendar and flips each planned square from magenta to green as it lands.
The Contribution Doctor
A preflight that runs before every execute and catches the reasons squares stay dark. Each check is one line with a status tag, the same lexicon as the log:
- [ OK ]Repository accessRetroFill can reach the repositories you gave it and nothing else on your account — or a note that you will set that up right before the push.
- [ .. ]Undo capabilityUndo deletes the repository RetroFill created, and can reach nothing else.
- [ OK ]Commit attributionCommits use <id>+<login>@users.noreply.github.com, which GitHub always links to your account.
- [ !! ]Account ageAccounts under 30 days old get a warning: sudden bulk activity can trip GitHub's spam heuristics. Start smaller.
- [ ERR ]Repository nameFails if <login>/<name> already exists. We never write to an existing repository.
- [ .. ]Private contributionsPrivate repo commits only show if “Include private contributions” is on in your profile settings.
- [ OK ]Default branchWe push to main, which becomes the default branch of the new repo. Only default-branch commits count.
- [ .. ]Graph refreshGitHub usually updates the graph within minutes; the docs say up to 24 hours.
A failing check blocks execution; a warning asks you to confirm.
Undo: delete the repo, nothing else
Every fill is a job with an UNDO button. Undo deletes the repositories that job created — all of them, if the plan was spread — marks the job undone, and GitHub recomputes your graph within its refresh window. It uses the same access you granted for the push, so it can delete those repositories and reach nothing else.
Because undo is repository deletion rather than history rewriting, it cannot touch anything that existed before RetroFill. There is no force-push, no branch surgery, and no way for it to reach your other repositories.
Autopilot: streak insurance, not a bot farm
Autopilot keeps the graph green with a few human-looking commits, on the rhythm you choose: every day (at least one commit on each day you missed, so a streak never breaks) or natural (the persona decides which days are active, so some days stay quiet, like a real week). An hourly job looks at each subscriber’s day in their own timezone, draws a seeded plan shaped by the persona they chose — how many commits, at which human hours — and performs the commits whose time has passed through GitHub’s API, into a journal repository it creates on its first run. It never writes into a repository you already had: if the name is taken, it asks you to pick another.
- It skips any day where you already contributed for real. It never doubles up.
- Commits land only after their scheduled local time, so a 21:40 commit is not there at 09:00.
- Pause or cancel in one click. Commits stop at the end of the paid period; what was pushed stays unless you undo it.
Limits and safety
- At most 30 commits on a single day and 20,000 per fill. Human scale, on purpose.
- One running job per account; new fills are limited to ten per hour.
- Public graph previews are limited to 30 per minute per IP address (120 when signed in), with a 10-minute cache per username and range.
- Accounts younger than 30 days get a warning before their first fill.
- We never write to a repository we did not create in that job, and we refuse names that already exist.
Where it runs
Next.js in a container on Railway, a SQLite database on its own volume, Stripe for payments — we never see card numbers — and no third-party tracking scripts. Sessions are a signed cookie; GitHub tokens are encrypted at rest and deleted the moment you delete your account. The realism engine, scheduler and text-art renderer are the same code in the browser and on the server.
Want the plain-English version? Read the transparency page, or the FAQ.
Built for the grid
Attribution, timezones, undo, realism — the parts that are easy to get wrong, handled, with a paintbrush on top.
Realism engine
Weekday bias, energy curves, streak momentum, vacations, a holiday dip and heavy-tailed commit counts. Nobody's graph looks like a barcode.
Pixel & text art
Type a word. It renders across the year in a 5×7 pixel font, calibrated to your real intensity levels so the letters actually read.
Gap-only fill
Respect existing is on by default: we only add on days you were empty. Your real commits are never touched, moved or overwritten.
Fast fill
A pure-JS git engine builds the whole repo and pushes it once, usually in under a minute, with a live terminal log the whole way.
One-click undo
Every fill is a job in History with an UNDO button. It deletes the generated repo; GitHub clears the squares within its refresh window.
Inspectable real repos
Commits touch a small, plausible project — TIL notes, a utility library or a dev journal — with varied messages and real diffs. Not an activity-log file.
Autopilot streak insurance
A few human-looking commits, only on days you missed, at human hours in your timezone. The streak stays alive while you sleep.
Timezone-correct
Timestamps carry the offset you confirm and sit inside a safe window, so every square lands on the day you painted — not the day before.
Ready to go green?
Free to paint. A few dollars to push. One click to undo.
Connect GitHub> press CONNECT to begin · read-only sign-in