Make 3am commit binges look like responsible adult behavior
Project description
git-spreader
A tool to make 3am commit binges look like responsible adult behavior — because the lie the contribution graph tells should at least be a plausible one.
Takes compressed bursts of commits (weekend hackathons, late-night sessions) and respaces them across realistic working hours — with jitter, flow-state clustering, occasional late-night/weekend commits, and random days off.
Installation
pip install git-spreader
# or
uv pip install git-spreader
Usage
Preview a schedule (dry run)
git-spreader preview HEAD~10..HEAD --start 2025-01-01 --seed 42
# Original Date New Date Score Gap Summary
1 2025-02-15 02:14:33 2025-01-02 09:23:41 0.12 — fix: typo in README
2 2025-02-15 02:31:07 2025-01-02 09:48:15 0.08 25m chore: update deps
3 2025-02-15 03:45:22 2025-01-06 10:12:33 0.73 1d 25m feat: add auth module
4 2025-02-15 04:02:19 2025-01-06 14:45:08 0.45 4h 33m feat: auth tests
5 2025-02-15 04:15:41 2025-01-07 09:37:22 0.91 18h 52m refactor: database layer
Rewrite timestamps
git-spreader spread HEAD~10..HEAD --start 2025-01-01 --seed 42
Set a fixed date range
git-spreader spread HEAD~20..HEAD --start 2025-01-01 --end 2025-01-31
Override working hours and days
git-spreader spread HEAD~10..HEAD --start 2025-02-01 --working-hours 08:00-16:00
git-spreader spread HEAD~10..HEAD --start 2025-02-01 --working-days Mon,Wed,Fri,Sat,Sun
Profiles
Built-in presets for common scheduling patterns:
| Profile | Hours | Days | Description |
|---|---|---|---|
default |
09:00–17:00 | Mon–Fri | Standard office hours |
night-owl |
22:00–04:00 | Mon–Sun | Late-night hacker |
side-project |
18:00–23:00 | Mon–Sun | Evenings after work/school |
weekend-warrior |
09:00–18:00 | Sat–Sun | Weekends only |
git-spreader spread HEAD~10..HEAD --start 2025-02-01 --profile side-project
Explicit CLI flags override profile values:
# Use side-project profile but change hours to 20:00-23:00
git-spreader spread HEAD~10..HEAD --start 2025-02-01 --profile side-project --working-hours 20:00-23:00
Manage configuration
git-spreader config --show # print effective config
git-spreader config --reset # reset to defaults
How It Works
Complexity scoring
Each commit is scored by a weighted combination of lines changed (50%), files touched (30%), and diff size in bytes (20%). The score is mapped through a curve (sqrt by default) to a time gap:
gap = min_gap + (max_gap - min_gap) * sqrt(score)
A typo fix gets a 10-minute gap. A 500-line refactor gets hours.
Scheduling
Gaps are placed within configurable working hours (default 09:00–17:00, Mon–Fri). When a gap crosses a day boundary, the commit wraps to the next available working day.
Realism engine
Six modifiers make the schedule look human:
| Modifier | Phase | Effect |
|---|---|---|
| Holidays | Pre-schedule | Removes holiday dates (US, UK, CA, etc.) |
| Days off | Pre-schedule | Randomly skips ~10% of workdays |
| Flow state | Post-schedule | Clusters 2–4 related commits into tight bursts |
| Late night | Post-schedule | Moves ~5% of low-complexity commits to 21:00–01:00 |
| Weekend | Post-schedule | Adds 1–3 commits to ~8% of weekend days |
| Jitter | Post-schedule | Random ±5–10 min offset on all timestamps |
All modifiers are individually configurable and can be disabled.
Backup and undo
Before rewriting, a backup ref is created at refs/spreader-backup/<timestamp>. To undo:
git reset --hard refs/spreader-backup/<timestamp>
Configuration
Configuration uses a three-tier precedence model: CLI flags > repo .git-spreader.toml > global ~/.config/git-spreader/config.toml > defaults.
Example .git-spreader.toml:
[schedule]
working_hours = { start = "09:00", end = "17:00" }
working_days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
timezone = "America/Los_Angeles"
[realism]
late_night_probability = 0.05
weekend_probability = 0.08
random_day_off_probability = 0.10
flow_state_clustering = true
avoid_holidays = true
[realism.jitter]
min_offset_minutes = -5
max_offset_minutes = 10
[realism.holidays]
calendar = "US"
additional = ["2025-12-24", "2025-12-26"]
[complexity]
weights = { lines = 0.5, files = 0.3, bytes = 0.2 }
min_gap_minutes = 10
max_gap_minutes = 480
curve = "sqrt" # or "linear", "log"
Development
uv sync
uv run pytest
uv run ruff check src/ tests/
uv run pyright src/
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file git_spreader-0.2.1.tar.gz.
File metadata
- Download URL: git_spreader-0.2.1.tar.gz
- Upload date:
- Size: 42.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
290290c9202c95a60486cc82a1b8f02e0e0ce6fac26f13e03bf899bf8a1fff04
|
|
| MD5 |
8df81894cb49f11b34156d50b06ea05c
|
|
| BLAKE2b-256 |
8650f98f8a9c986bb2ec344ae5b1da18ed9dec044e7e382696f555496549a3b3
|
Provenance
The following attestation bundles were made for git_spreader-0.2.1.tar.gz:
Publisher:
publish.yml on lokkju/git-spreader
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
git_spreader-0.2.1.tar.gz -
Subject digest:
290290c9202c95a60486cc82a1b8f02e0e0ce6fac26f13e03bf899bf8a1fff04 - Sigstore transparency entry: 995294963
- Sigstore integration time:
-
Permalink:
lokkju/git-spreader@29a74bd0bdc28f14c1b04852497f3674fd17ccfb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/lokkju
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@29a74bd0bdc28f14c1b04852497f3674fd17ccfb -
Trigger Event:
push
-
Statement type:
File details
Details for the file git_spreader-0.2.1-py3-none-any.whl.
File metadata
- Download URL: git_spreader-0.2.1-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61d788e8bf24b2a21c42a5978c60c77d0fca0c8832e30240dd7c0d2319c3f87f
|
|
| MD5 |
ffdf784f27f1cebd3dfceb148260c552
|
|
| BLAKE2b-256 |
ab053ee593df5e2ee5dd3b7ae16f6189b5043872904499b8a17176abaef554f1
|
Provenance
The following attestation bundles were made for git_spreader-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on lokkju/git-spreader
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
git_spreader-0.2.1-py3-none-any.whl -
Subject digest:
61d788e8bf24b2a21c42a5978c60c77d0fca0c8832e30240dd7c0d2319c3f87f - Sigstore transparency entry: 995294993
- Sigstore integration time:
-
Permalink:
lokkju/git-spreader@29a74bd0bdc28f14c1b04852497f3674fd17ccfb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/lokkju
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@29a74bd0bdc28f14c1b04852497f3674fd17ccfb -
Trigger Event:
push
-
Statement type: