Like .gitignore, but for what goes public. Keep a sanitized public mirror of your private repo.
Project description
git-private2public
Like .gitignore, but for what goes public.
Need the full rule-by-rule explanation? Read Advanced configuration / RU.
You have a private repo. You want a public one — without the secrets. This tool keeps them in sync. Automatically.
Quick start
pip install git-private2public
git-private2public init # creates .gitpublic/ folder
Edit .gitpublic/config — set source + target. Values can be owner/repo, a full Git URL, or a local path:
source = you/private-repo
target = you/public-repo
Edit .gitpublic/ignore — files to hide, one per line (like .gitignore):
.env
secrets/
*.key
Publish:
git-private2public publish
Done. Your public repo is clean.
Auto-publish on every git push
git-private2public hook enable # on
git push # also publishes public mirror
git-private2public hook disable # off
Native git hook. No CI, no GitHub Actions. Works offline.
Guard — pre-push safety net
guard installs a lightweight pre-push hook that blocks git push if
scanned content matches known secret patterns. Unlike hook (which also
rewrites history and force-pushes a public mirror), guard is purely a
refusal mechanism — no clone, no filter-repo, no push.
git-private2public guard enable # install pre-push hook
git-private2public guard status # is it on?
git-private2public guard disable # remove the hook
git-private2public guard run # manual scan (also what the hook does)
What it scans — by default:
- Every tracked file in the working tree (
git ls-files). - Every blob in git history — catches secrets committed in old commits
and later removed from HEAD but never rewritten via filter-repo.
Use
--no-historyto skip this (faster, but won't catch old leaks).
Default secret patterns (always on, no .gitpublic/ required):
- OpenAI and Anthropic:
sk-...,sk-proj-...,sk-ant-... - GitHub:
ghp_...,github_pat_...,gho_...,ghs_...,ghr_... - HuggingFace and Slack:
hf_...,xox[baprs]-... - AWS long-term and temporary access key IDs:
AKIA...,ASIA... - Google API/OAuth keys and GitLab PATs
- Telegram bot, npm, PyPI, Stripe, SendGrid and Discord tokens
- Twilio, Mailgun and DigitalOcean API credentials
- Generic JWTs and PEM private-key headers
Custom patterns: drop them into .gitpublic/scan, one per line (literal
or regex:...). They layer on top of the defaults.
Allowlist: anything in .gitpublic/allow is treated as an exception for
broad regex rules that happen to match a public domain.
Bypass for one push (NOT recommended):
GIT_PRIVATE2PUBLIC_SKIP_GUARD=1 git push
What guard tells you when it refuses the push:
The error message is tailored to where the secret lives:
- In the working tree — points at the file, suggests editing,
.gitignore+git rm --cached, and reminds you to rotate a live secret before committing. - In git history — explains that editing HEAD isn't enough, and
prints the exact next step:
git-private2public publish(if you have a.gitpublic/set up), orgit filter-repo --replace-text replacements.txt --force(manual), with thereplacements.txtformat.
When both happen, you get both sections.
If you only want guard and not hook (publish) — guard enable works
independently. They share the same pre-push file, so guard enable refuses
to install if hook is already there; disable one before enabling the
other.
Why scan history?
A secret leaked into a commit last month, removed from HEAD yesterday, but
the commit object still exists in .git/objects/. Pushing HEAD won't
expose it directly, but anyone who already has the repo and runs
git log -p will see it. Guard's history scan catches this. To actually
remove it from history, run git-private2public publish (which uses
filter-repo), or git filter-repo --replace-text manually.
The .gitpublic/ folder
Each file is one concern. Like .gitignore — one rule per line, # for
comments. If a file is missing, that setting is just empty.
| File | What goes in it | Format |
|---|---|---|
config |
source, target, push settings | key = value |
ignore |
files to NOT publish | one path/glob per line |
replace |
find → replace in file contents | old ==> new per line |
scan |
refuse to push if matched | one pattern per line |
allow |
exceptions for domain rules in scan |
one allowed matched domain per line |
Easy — just edit ignore:
.env
secrets/
*.key
Medium — also edit replace:
<PRIVATE_IP> ==> 203.0.113.5
real-token ==> ***
regex:[A-Fa-f0-9]{64} ==> ***
Hard — also edit scan + allow:
# scan:
regex:github_pat_[A-Za-z0-9_]{30,}
regex:192\.168\.
regex:[a-z0-9.-]+\.[a-z]{2,}
# allow:
github.com
get.docker.com
Commands
init create .gitpublic/ config
scan clean into a temp repo, scan, don't push
publish clean + push
hook enable / disable / status
guard enable / disable / status / run (pre-push secret scanner)
How allow / domains work
Nothing is auto-blocked just because it is a domain.
allow is an exception list for scan. If .gitpublic/scan is missing or empty, allow does nothing and domains are not checked at all.
To block domain-looking strings, add a broad domain rule to .gitpublic/scan:
regex:[a-z0-9.-]+\.[a-z]{2,}
Now every matched domain fails the scan unless the matched domain itself is listed in .gitpublic/allow:
github.com
get.docker.com
example.com
allow does not replace private domains. Use .gitpublic/replace for that:
private.company.local ==> example.com
regex:.*\.corp\.internal ==> example.com
Analogy: scan says “ban everything matching this pattern”, allow says “except these exact public domains”.
Rule of thumb:
| You want to... | File |
|---|---|
| remove files | .gitpublic/ignore |
| rewrite private text/domain/IP | .gitpublic/replace |
| fail if a secret/domain/IP survived | .gitpublic/scan |
| make exceptions for public domains caught by scan | .gitpublic/allow |
More examples: Advanced configuration / RU.
Install
pip install git-private2public
That's it. Now you have the git-private2public command.
No pip? Single-file manual install — download +
chmod +x(needspip install git-filter-repo pyyaml).
Why
Git has no "private file in a public repo". So you need two repos. This keeps them in sync — without leaking.
| delete files | replace text | scan | auto push | pre-push guard | |
|---|---|---|---|---|---|
| git-filter-repo | ✅ | ✅ | ❌ | ❌ | ❌ |
| BFG | ✅ | ✅ | ❌ | ❌ | ❌ |
| dupligit | ❌ | ❌ | ❌ | ✅ | ❌ |
| gitleaks / trufflehog | ❌ | ❌ | ✅ | ❌ | ✅ (separate tool) |
| git-private2public | ✅ | ✅ | ✅ | ✅ | ✅ (built-in) |
License
MIT
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_private2public-0.1.8.tar.gz.
File metadata
- Download URL: git_private2public-0.1.8.tar.gz
- Upload date:
- Size: 31.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6e24a8b301c4ade9aaea41fa8b1dd484bd5b62b66b747ec2b2117c164380f92
|
|
| MD5 |
82f6c1c1beba3ba45d6e3ccfd2d859f7
|
|
| BLAKE2b-256 |
6ef0c25a6a3b96f8c14bf83c1869980f4ac510368ca2b58f772dbe60a16f450f
|
File details
Details for the file git_private2public-0.1.8-py3-none-any.whl.
File metadata
- Download URL: git_private2public-0.1.8-py3-none-any.whl
- Upload date:
- Size: 19.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43df6c31031a74a15c94716c6d87652bd73fe9a705b0f00a1a594c7734efb7a4
|
|
| MD5 |
ff92534c6b6221858eaddf6d4febaccc
|
|
| BLAKE2b-256 |
818587400722c62701be2f0776424b5e62173d97df108fa4e4f7645011981791
|