Helps you stay focused on what you're supposed to be doing.
A git pre-commit hook that blocks commits during configured focus windows. Overridable when you genuinely need to ship. Stealth-installable so it doesn't pollute your repo.
By a puppygirl.
cp leash ~/.local/bin/ && chmod +x ~/.local/bin/leash
# or install directly from GitHub
# curl -s https://raw.githubusercontent.com/SiteRelEnby/git-leash/refs/heads/main/leash > ~/.local/bin/leash && \
chmod +x ~/.local/bin/leashMake sure ~/.local/bin is in your PATH.
Then, in any repo:
# install the hook to this repo
leash install
# or install globally (all repos)
leash install --globalleash install automatically adds .leash and .leash-slip to .git/info/exclude — no .gitignore modifications, nothing tracked, pure stealth.
Place at ~/.leash (global) or .leash in your repo root (project-local overrides global).
[schedule "work-hours"]
days=mon,tue,wed,thu,fri
start=09:00
end=17:00
timezone=local
# only block personal repos — work repos are fine
block=remote:github.com/myuser/*
block=dir:side-project
task=finish the auth refactor
[schedule "go-to-bed"]
days=mon,tue,wed,thu,sun
start=23:00
end=06:00
task=go to sleep you disaster
# no filter = block everything. go to sleep.
[task]
# global fallback when a schedule doesn't have its own
current=stop getting distracted
[messages]
tone=puppy
[override]
env_var=UNLEASH
slip_file=.leash-slipRun leash example for a fully commented config.
Named blocks that define when commits are restricted. Multiple schedules stack — if any match, the commit is blocked.
days— comma-separated, lowercase (default:mon,tue,wed,thu,fri)start/end— 24h time. Overnight windows work (23:00to06:00)timezone—localor IANA zone likeAmerica/New_York
Without filters, a schedule blocks all repos. Add filters to be selective.
allow= (allowlist) — only matching repos can commit during this window, everything else is blocked. This is the one you probably want for "let me commit to work repos during work hours":
[schedule "work-hours"]
days=mon,tue,wed,thu,fri
start=09:00
end=17:00
allow=remote:github.com/company/*
allow=path:/home/user/work/*
allow=dir:work-projectblock= (denylist) — only matching repos are blocked, everything else is fine:
block=remote:github.com/myuser/*
block=dir:side-projectPick one per schedule. If both allow= and block= are present, allow= wins and block= is ignored (leash will warn you about this). Set suppress_warnings=true in [messages] to silence the warning if you know what you're doing.
Prefixes (required):
| Prefix | Matches against |
|---|---|
remote: |
All remote URLs (not just origin) — protocol and .git suffix stripped |
path: |
Repo root absolute path |
dir: |
Repo directory name (basename) |
Multiple lines of the same type stack as OR — any match counts.
Optional reminder shown in the block message. Can be set per-schedule or globally as a fallback:
[schedule "work-hours"]
task=finish the auth refactor
[schedule "go-to-bed"]
task=go to sleep you disaster
[task]
# fallback when a schedule doesn't have its own
current=stop getting distractedSet the global task from the CLI:
leash task "finish the auth refactor"[messages]
tone=default # normal focus-helper vibes
tone=puppy # i know what you areWhen you genuinely need to commit during a focus window:
# environment variable (configurable name)
UNLEASH=1 git commit -m "it's fine"
# one-time pass — auto-deleted after one commit
leash slip
# nuclear option (skips all git hooks)
git commit --no-verifyleash install [--global] Install the pre-commit hook
leash uninstall [--global] Remove the pre-commit hook
leash slip Create a one-time commit pass
leash status Show config, schedule, and block status
leash task [description] Show or set the current task reminder
leash check Run the hook check manually
leash example Print a fully commented example config
leash help Show help
leash version Show version
leash install writes a pre-commit hook that calls back to the leash script. On each git commit:
- Check overrides (env var, slip file) — fast exit if set
- Read config (
~/.leash+.leash, project wins) - Check each schedule: day, time, repo filters
- If blocked: bark, show task, print override hints, exit 1
The hook remembers where leash was installed from, but also checks PATH as a fallback. If leash can't be found at all, it warns but allows the commit — it won't silently break your workflow.
The whole point is that this doesn't leave traces in your repo:
.leashand.leash-slipare added to.git/info/exclude(repo-local gitignore, untracked)- The hook lives in
.git/hooks/(not tracked) - Nothing touches
.gitignore
If there's an existing pre-commit hook, it gets backed up to pre-commit.leash-backup and chained — leash runs first, then your original hook.
Bash 4+ and coreutils. That's it.
BSD 3-Clause