CLI tool for finding surviving forks of deleted GitHub repositories.
Uses the GitHub Search API to find public forks by repository name, clusters them by description to minimize API calls, and verifies ownership via the source/parent fields. Optionally clones found forks as bare repositories.
uv venv
uv sync# Search forks by URL or owner/repo
uv run fork-search https://github.com/deleted-user/some-repo
# Multiple repositories at once
uv run fork-search owner/repo1 owner/repo2
# JSON output for piping
uv run fork-search owner/repo --json
# Interactive cluster selection
uv run fork-search owner/repo --pick
# Deep search (bypass the 1000-result cap)
uv run fork-search owner/repo --deep
# Clone the best (most recently pushed) fork
uv run fork-search owner/repo --clone
# Clone all found forks
uv run fork-search owner/repo --clone-all --out ./backupsToken is passed via the GITHUB_TOKEN environment variable. Can be placed in a .env file in the project root.
GITHUB_TOKEN=ghp_...
Without a token, rate limits are 60 requests/hour (core) and ~10 requests/min (search).
A token with public_repo (read-only) scope is sufficient. A fine-grained token with no additional permissions also works.
| Flag | Description |
|---|---|
--clone |
Clone the best fork (bare) into --out |
--clone-all |
Clone every found fork |
--out DIR |
Output directory for clones (default: ./mirror) |
--pick |
Interactive TUI cluster selection |
--top N |
Show top N forks (default: 10, 0 = all) |
--sort date|stars |
Sort by: date (default) or stars |
--broad |
Search name + description + README (default: name only) |
--deep |
Bypass the 1000-result cap via date bisection |
--created RANGE |
Filter by creation date, format 2020-01-01..2025-12-31 |
--json |
Output results as JSON (stdout) |
-v |
DEBUG-level logging |
--version |
Show version |
- Search. Query
{repo} in:name fork:truevia GitHub Search API. Paginate up to 1000 results (or more with--deep). - Filter. Keep only items with
fork: truein the JSON response. - Cluster. Group forks by description. This reduces the number of verification API calls: one representative per cluster instead of every fork.
- Verify. For each cluster's representative, fetch
/repos/{full_name}and check thatsource.full_namematches the target repository. If the first fork in a cluster is unavailable (404), the next ones are tried. - Output. Colored table (rich) or JSON. With
--clone, runsgit clone --bare.
GitHub Search API limits results to 1000 per query. The --deep flag works around this: a probe request (per_page=1) fetches the total_count, and if it exceeds 1000, the created: date range is recursively bisected. Leaf ranges (<=1000) are fully paginated. Bisection branches run in parallel.
from fork_search.client import GitHubClient
from fork_search.resolver import ForkResolver
async with GitHubClient(token="ghp_...") as client:
resolver = ForkResolver(client)
record = await resolver.find_forks("owner", "repo")
for fork in record.forks:
print(fork.fork_full_name, fork.pushed_at)- Only finds public forks.
- GitHub Search API returns at most 1000 results per query. The
--deepflag works around this via date bisection, but increases the number of API calls. - Description-based clustering may group unrelated repositories with identical descriptions. Use
--pickfor manual filtering.