-
Notifications
You must be signed in to change notification settings - Fork 0
CODE_WIKI English version
Mirror-accelerated
git clonetool that automatically resetsremoteto the official address after cloning. Core value: Download from mirror sites (fast), subsequentpull/pushgo through the official repo (secure). Zero external dependencies: Uses only the Python standard library.
- 1. Project Overview
- 2. Project Architecture
- 3. Directory Structure
- 4. Core Module Responsibilities
-
5. Key Classes and Functions
- 5.1 Config Loading (fastclone.py)
- 5.2 URL Parsing and Platform Detection
- 5.3 Mirror URL Transformation (4 Strategies)
- 5.4 IPv4/IPv6 Network Protocol Detection
- 5.5 Speed Test and Cache
- 5.6 Clone Speed Monitoring
- 5.7 Main Clone Flow (Auto-Retry + Mirror Switching)
- 5.8 Terminal Output and Color Support
- 5.9 Setup Mode
- 5.10 Entry Point main()
- 5.11 Internationalization (i18n.py)
- 5.12 Daily Mirror Test Script (scripts/test_mirrors.py)
- 6. Dependencies
- 7. How to Run
- 8. Configuration Reference (mirror.json)
- 9. GitHub Actions Automation
- 10. Key Flow Diagrams
- 11. Design Highlights and Safety Mechanisms
fast-clone is a command-line tool written in Python that accelerates cloning GitHub / GitLab repositories under restricted network conditions. It downloads repository content through mirror sites (prefix proxy, domain replacement, etc.) and automatically resets the origin remote to the official address after cloning, balancing "fast downloads" with "safe subsequent pull/push".
| Feature | Description |
|---|---|
| Safe and worry-free | Automatically resets remote to official address after cloning |
| Auditable code | Pure-text Python source, no binaries, no obfuscation |
| Zero dependencies | Uses only the Python standard library, no pip install needed |
| Smart protection | Speed monitoring + auto-retry + direct-connect fallback |
| Multi-mirror support | 11 built-in mirrors, 4 transformation strategies |
| Network adaptive | Auto-detects local IPv4/IPv6 support, skips unavailable mirrors |
| Speed-test cache | Speed-test results reused within 7 days, avoiding repeated tests |
| Daily status report | GitHub Actions runs daily tests and publishes reports as Releases |
| Bilingual support | Auto-detects and switches between Chinese and English |
| Highly customizable | Edit mirror.json to add/remove mirrors, effective on next run |
License: MIT License, Copyright (c) 2026 bcggxx.
fast-clone adopts a minimal architecture of single-file core + separated config/i18n, divided into three layers:
┌─────────────────────────────────────────────────────────────┐
│ User CLI / CI │
│ fast-clone <url> [--fastest] [--mirror xxx] [-n] ... │
└───────────────┬─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Core Layer fastclone.py (single file, zero ext deps) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ Config │ │ URL │ │ Mirror │ │ IPv4/IPv6 │ │
│ │ Loading │ │ Parsing │ │ Transform│ │ Detection │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ Speed │ │ Speed │ │ Clone │ │ Terminal │ │
│ │ Test+Cache│ │ Monitor │ │ Flow │ │ Color Output │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Setup Mode (Windows PATH injection) / main() entry │ │
│ └──────────────────────────────────────────────────────┘ │
└──────┬──────────────────┬───────────────────┬───────────────┘
│ reads │ reads/writes │ imports
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ mirror.json │ │ speedcache/ │ │ i18n.py │
│ mirror cfg │ │ speed cache │ │ biling text │
└─────────────┘ └─────────────┘ └─────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CI Layer .github/workflows/mirror-test.yml │
│ Daily UTC 08:00 → scripts/test_mirrors.py → gen report │
│ → overwrite mirror-status Release │
└─────────────────────────────────────────────────────────────┘
Architecture characteristics:
-
Single-file core:
fastclone.pycontains all business logic (~1200 lines), no third-party dependencies. -
Config/code separation: Mirror list in
mirror.json, text ini18n.py, modifications don't touch core code. -
Runtime data isolation: Speed-test cache writes to
speedcache/(gitignored), doesn't pollute the repo. -
Platform wrappers:
windows/fast-clone.cmdand the wrapper created bylinux/setup.shboth point to the in-placefastclone.py, config paths remain unchanged.
fast-clone/ ← keep this directory fixed after install
├── fastclone.py ← core script (main logic, ~1200 lines)
├── i18n.py ← bilingual text and language detection
├── mirror.json ← mirror config (11 mirrors + default params)
├── scripts/
│ └── test_mirrors.py ← GitHub Actions connectivity test script
├── .github/workflows/
│ └── mirror-test.yml ← daily mirror status Release workflow
├── windows/
│ ├── setup.bat ← Windows install entry (calls --setup)
│ └── fast-clone.cmd ← Windows command wrapper
├── linux/
│ └── setup.sh ← Linux install script (creates wrapper)
├── README.md ← Chinese usage docs
├── README.en.md ← English usage docs
├── LICENSE ← MIT license
├── .gitignore ← ignores speedcache/, mirror-test-report.md, etc.
├── speedcache/ ← (runtime-generated) speed-test cache dir
└── mirror-test-report.md ← (CI-generated) test report, gitignored
| Module | Responsibility | Independently editable |
|---|---|---|
fastclone.py |
Core business logic: arg parsing, URL transformation, IP detection, speed test, clone monitoring, auto-retry, remote reset, Setup mode | Yes (core code) |
i18n.py |
System language detection (Windows LCID / Unix env vars) + bilingual text table _T + L()/Lh() translation functions |
Yes (text isolated) |
mirror.json |
Mirror config: default mirror, speed threshold, timeout, retry count, mirror dict (each mirror has name/platforms/transform/test_host/ip, etc.) | Yes (edit takes effect immediately) |
scripts/test_mirrors.py |
CI-only: reads mirror.json, concurrent TCP 443 speed test, generates bilingual Markdown report, read-only, never modifies mirror.json |
Yes |
.github/workflows/mirror-test.yml |
Daily scheduled task: runs speed-test script → overwrites mirror-status Release |
Yes |
windows/setup.bat |
Windows install entry, checks Python then calls fastclone.py --setup
|
Yes |
windows/fast-clone.cmd |
Windows command wrapper: detects Python and forwards args to fastclone.py
|
Yes |
linux/setup.sh |
Linux install script: checks Python/Git, creates wrapper script pointing to in-place fastclone.py
|
Yes |
Config is loaded from mirror.json in the same directory as fastclone.py, read once at startup and cached as module-level variable _CONFIG.
| Function | Location | Description |
|---|---|---|
_load_mirror_config() |
fastclone.py#L49 | Loads config from mirror.json; falls back to empty-mirror config if file missing/corrupt, ensuring the tool can still direct-clone |
load_config() |
fastclone.py#L81 | Returns the cached _CONFIG dict |
get_mirrors(config) |
fastclone.py#L85 | Gets the mirrors sub-dict |
get_default_mirror(config) |
fastclone.py#L89 | Gets default mirror key, defaults to 'gh-proxy-org'
|
get_speed_threshold(config) |
fastclone.py#L93 | Gets speed threshold (KiB/s), defaults to 1024 (1 MB/s) |
get_speed_timeout(config) |
fastclone.py#L97 | Gets speed timeout seconds, defaults to 180 |
get_connect_retries(config) |
fastclone.py#L101 | Gets connection retry count, defaults to 3 |
mirror.json top-level fields:
{
"default": "gh-proxy-org",
"speed_threshold_kib": 1024,
"speed_timeout_seconds": 180,
"connect_retries": 3,
"mirrors": { ... }
}Parses user-input repository URLs (HTTPS or SSH) into structured info for subsequent mirror transformation.
| Function | Location | Description |
|---|---|---|
parse_git_url(url) |
fastclone.py#L127 | Parses git URL, supports both git@host:path (SSH) and https://host/path formats; returns dict with original/official/domain/owner/repo/platform/is_ssh/is_wiki/https_url
|
_split_path(path) |
fastclone.py#L109 | Splits path into (owner, repo, is_wiki); recognizes .wiki / .wiki.git suffix for Wiki repos, Wiki clone URLs don't get .git appended |
_make_info(...) |
fastclone.py#L142 | Assembles parsed result dict, generates normalized https_url and display official address |
_detect_platform(domain) |
fastclone.py#L159 | Identifies platform from domain: github / gitlab (incl. jihulab) / gitee / unknown
|
Returned info dict structure:
{
'original': 'git@github.com:user/repo.git', # original input
'official': 'git@github.com:user/repo', # display addr without .git
'domain': 'github.com',
'owner': 'user',
'repo': 'repo',
'platform': 'github',
'is_ssh': True,
'is_wiki': False,
'https_url': 'https://github.com/user/repo.git' # base URL for mirror transform
}apply_mirror(info, mirror) (fastclone.py#L174) selects a strategy based on mirror['transform'] field, converting the official HTTPS URL to a mirror URL:
| Strategy | Required fields | Transformation example |
|---|---|---|
prefix |
prefix |
https://mirror.com/ + https://github.com/user/repo.git
|
domain_replace |
old, new
|
github.com → kkgithub.com (replaces first match only) |
path_prefix |
prefix |
https://gitclone.com/github.com/ + user/repo (strips protocol and domain) |
domain_suffix |
old, suffix
|
github.com → github.com.mirror.org
|
Unknown strategy raises ValueError.
Detects local network protocol support at startup, used to filter unreachable mirrors (e.g., skip IPv6-only mirrors in environments without IPv6).
| Function / Variable | Location | Description |
|---|---|---|
_IP_SUPPORT |
fastclone.py#L199 | Module-level cache: (has_ipv4, has_ipv6) or None (not detected) |
_IPV4_PROBES / _IPV6_PROBES
|
fastclone.py#L204 | Probe endpoints: Cloudflare first, falls back to Tencent DNSPod (avoids false-negative when Cloudflare is blocked) |
_probe_ip(family, addr, timeout) |
fastclone.py#L208 | TCP 443 connection probe to specified address, returns bool |
_probe_ip_any(family, addrs, timeout) |
fastclone.py#L220 | Tries address list sequentially, returns True on first success |
detect_ip_support(timeout) |
fastclone.py#L228 | Parallel IPv4/IPv6 detection, result cached to _IP_SUPPORT; does not filter when both stacks fail (lets real clone decide); returns (has_v4, has_v6)
|
filter_mirrors_by_ip(mirrors, has_v4, has_v6) |
fastclone.py#L257 | Filters mirrors by mirror['ip'] field (dual/v4/v6); not applied when --mirror is explicit |
In --fastest mode, concurrent TCP speed tests pick the fastest mirror; results cached to speedcache/ directory, valid for 7 days.
| Function | Location | Description |
|---|---|---|
_CACHE_DIR |
fastclone.py#L283 | Cache directory: speedcache/ next to script |
_CACHE_EXPIRE_DAYS |
fastclone.py#L284 | Cache validity: 7 days |
_cache_dir() |
fastclone.py#L287 | Ensures cache dir exists and returns it |
_cache_filename(dt) |
fastclone.py#L293 | Cache filename = test time (YYYY-MM-DD_HHMMSS.json, Windows-safe no colons) |
_find_valid_cache(platform) |
fastclone.py#L298 | Finds the most recent valid (non-expired) cache entry for platform, None if not found |
_save_cache(platform, results) |
fastclone.py#L325 | Persists speed-test results, filename is test timestamp |
_find_expired_caches() |
fastclone.py#L339 | Lists expired (>7 days) or corrupt cache files |
prompt_delete_expired_caches() |
fastclone.py#L362 | Asks at startup whether to delete expired caches (default Y delete) |
_tcp_latency(host, port, timeout) |
fastclone.py#L386 | Single mirror TCP 443 latency test, returns float('inf') on failure |
_get_speed_results(platform, candidates, timeout) |
fastclone.py#L396 | Speed-test main logic: check cache first → if miss, concurrent live test → write cache; returns {mirror_key: latency_seconds}
|
find_fastest_mirror(info, mirrors, config, timeout) |
fastclone.py#L445 | Selects fastest mirror: filter by platform → IP filter → speed test → pick lowest latency; falls back to default mirror if all unreachable |
Cache file format:
{
"test_time": "2026-07-06T05:54:54",
"platform": "github",
"results": {
"gh-proxy-org": {"latency_ms": 188.0, "reachable": true},
"kkgithub": {"latency_ms": null, "reachable": false}
}
}During cloning, parses git clone --progress stderr output in real-time, monitors download speed, aborts and switches mirror if low speed persists beyond timeout.
| Function / Class | Location | Description |
|---|---|---|
_SPEED_RE |
fastclone.py#L478 | Speed regex: matches 123.45 MiB/s or 678 KiB/s
|
_LOCAL_PHASES |
fastclone.py#L479 | Local phase keywords (bilingual): updating files / 更新文件 etc., these phases don't participate in speed judgment |
_CONN_ERRS |
fastclone.py#L486 | Connection error keyword list (could not resolve host / connection timed out, etc.) |
_PROGRESS_SIZE_RE |
fastclone.py#L495 | Progress regex: matches 12.3 MiB | 1.5 MiB/s (cumulative size + current speed) |
_render_progress(line) |
fastclone.py#L508 | Renders git stderr lines into human-readable real-time progress (received object size + speed), TTY-only display |
_parse_speed(line) |
fastclone.py#L552 | Parses speed line, returns (speed_kib, is_local_phase)
|
_is_connection_error(text) |
fastclone.py#L564 | Determines if it's a connection-class error (used to distinguish retry vs switch) |
SpeedMonitor |
fastclone.py#L569 |
Speed monitoring class: thread-safe tracking of last speed-OK time; feed(line) feeds log lines, should_abort() decides whether to abort, stalled property returns stalled seconds |
_stderr_reader(proc, monitor, collected, abort) |
fastclone.py#L596 | Background thread: reads git stderr, feeds monitor, renders progress, triggers abort |
_safe_kill(proc) |
fastclone.py#L616 | Cross-platform safe process kill: Windows uses taskkill /T, Linux uses process group SIGTERM→SIGKILL
|
clone_with_monitor(mirror_url, target_dir, clone_args, min_kib, timeout_sec) |
fastclone.py#L635 |
Single clone execution: starts git clone --progress, monitors speed, returns status dict (ok/speed_timeout/connection_error/other_error) |
SpeedMonitor class:
class SpeedMonitor:
def __init__(self, min_kib, timeout_sec): ...
def feed(self, line): ... # feed log line, refresh _ok time if meets threshold
def should_abort(self) -> bool: # True if stalled beyond timeout
@property
def stalled(self) -> float: # current stalled seconds| Function | Location | Description |
|---|---|---|
clone_with_fallback(info, url, args, config, mirror_keys) |
fastclone.py#L694 |
Core clone flow: iterates mirror list, each mirror retries per connect_retries; on success resets remote and returns; speed timeout switches to next; connection errors retry; all failures fall back to direct official clone |
_set_remote(tp, url) |
fastclone.py#L794 | Resets origin remote to official address after successful clone (SSH addresses also set back to original SSH) |
_resolve_mirror_list(args, info, config) |
fastclone.py#L805 |
Resolves mirror candidate list: filter by platform → IP filter (when not --mirror) → --fastest speed-test sort / --mirror specified first / default mirror first; --dry-run skips speed test and cache writes |
_build_clone_args(args, url) |
fastclone.py#L1181 | Builds git clone args (--progress + branch/depth/single-branch + url + target + extra) |
clone_with_fallback state machine:
res['status'] |
Meaning | Handling |
|---|---|---|
ok |
Clone succeeded | Reset remote, return 0 |
speed_timeout |
Speed persistently below threshold | Switch to next mirror (no retry) |
connection_error |
Connection-class error | Retry (up to connect_retries times), switch when exhausted |
other_error |
Other errors (incl. git not installed) | Switch to next mirror |
Safety protections: refuses to clone into current working directory; refuses to delete current working directory (prevents accidental user data loss).
| Function / Class | Location | Description |
|---|---|---|
Color |
fastclone.py#L842 |
Color output class: defines ANSI color codes; enable() enables VT mode on Windows (SetConsoleMode), Unix checks isatty(); c(text, code) wraps color |
Color._enabled |
fastclone.py#L870 | One-time determination at module load whether to enable colors |
print_separator() |
fastclone.py#L873 | Prints cyan separator line |
print_header(title) |
fastclone.py#L877 | Prints separator block with title |
print_step(msg) |
fastclone.py#L883 | Cyan -> step hint |
print_ok(msg) |
fastclone.py#L887 | Green OK success hint |
print_warn(msg) |
fastclone.py#L891 | Yellow ! warning |
print_err(msg) |
fastclone.py#L895 | Red X error |
die(msg, code) |
fastclone.py#L899 | Prints error then sys.exit(code)
|
run_git(args, **kw) |
fastclone.py#L904 | Wraps subprocess.run(['git'] + args)
|
On Windows, setup.bat → fastclone.py --setup triggers interactive installation.
| Function | Location | Description |
|---|---|---|
_ps_quote(s) |
fastclone.py#L912 | PowerShell safe single-quote escaping |
_add_to_path(target, machine) |
fastclone.py#L917 | Uses PowerShell [Environment]::SetEnvironmentVariable to add directory to user/system PATH (avoids setx 1024-char truncation), deduplicates, returns 'ok'/'already'/'fail'
|
_cmd_setup() |
fastclone.py#L959 | Interactive install flow: check Git → choose PATH install method (user/system/manual) → add PATH → verify (list mirrors) → done |
main() (fastclone.py#L1040) is the program entry, flow:
- Load config, build
argparseparser (with full args and bilingual help) -
--setup→ go to install flow -
--list-mirrors→ list all mirrors - No
url→ print help -
parse_git_url()parses URL - Unknown platform → warn and direct-clone
-
_resolve_mirror_list()resolves mirror candidates - Print repository info (platform/repo name/official URL/preferred mirror/fallback mirrors, etc.)
-
--dry-run→ preview URL transformation, no clone -
prompt_delete_expired_caches()asks to clean expired caches -
clone_with_fallback()executes clone
Complete CLI arguments:
| Argument | Short | Description |
|---|---|---|
url |
— | Official repository URL |
--mirror |
-m |
Specify mirror key |
--fastest |
-f |
Auto speed-test, pick fastest |
--timeout |
-t |
Speed-test timeout seconds (default 3) |
--list-mirrors |
-l |
List all mirrors |
--branch |
-b |
Specify branch |
--depth |
-d |
Shallow clone depth |
--single-branch |
— | Single branch only |
--target |
— | Target directory name |
--min-speed |
— | Minimum speed MB/s |
--speed-timeout |
— | Speed timeout seconds |
--no-set-url |
— | Do not reset remote (debug) |
--dry-run |
-n |
Preview without cloning |
--setup |
— | (hidden) Setup mode |
--help |
-h |
Help |
| Function / Variable | Location | Description |
|---|---|---|
detect_language() |
i18n.py#L19 | System language detection: ① env var FASTCLONE_LANG takes priority; ② Windows uses GetUserDefaultUILanguage/GetSystemDefaultUILanguage/GetUserDefaultLCID (only recognizes zh-CN 0x0804, excludes zh-TW/HK); ③ Unix checks LC_ALL/LC_MESSAGES/LANG/LANGUAGE; ④ locale.getlocale(); ⑤ defaults to zh (tool ships CN mirrors, primarily targets CN users) |
LANG |
i18n.py#L94 | Module-level constant, detected once at startup |
_T |
i18n.py#L101 | Bilingual text dict: {key: {'zh': ..., 'en': ...}}, contains all user-visible text for mirror/repo/clone/speed-test/cache/IP/dry-run/error/setup |
L(key, *args) |
i18n.py#L213 | Looks up text by key and formats with args; falls back to English then to key itself when missing |
Lh(zh, en) |
i18n.py#L219 | One-off bilingual helper for ad-hoc strings like argparse help |
Language override: FASTCLONE_LANG accepts zh/cn/chinese/zh-cn/zh_cn/zh-hans and en/english.
CI-only script, read-only on mirror.json, never modifies it.
| Function / Variable | Location | Description |
|---|---|---|
MIRROR_JSON / REPORT_FILE
|
test_mirrors.py#L29 | Config and report paths (project root) |
_detect_runner_ipv6() |
test_mirrors.py#L32 | Probes whether CI runner supports IPv6 (Cloudflare anycast :443) |
RUNNER_HAS_IPV6 |
test_mirrors.py#L45 | Module-level constant, runner IPv6 capability |
load_mirrors() |
test_mirrors.py#L48 | Loads mirror.json
|
tcp_latency(host, port, timeout) |
test_mirrors.py#L53 | Single mirror TCP 443 latency test, returns (latency_ms, error)
|
test_mirror(key, mirror, timeout) |
test_mirrors.py#L64 | Tests single mirror: IPv6 mirrors skipped on runners without IPv6 |
main() |
test_mirrors.py#L75 | Concurrent speed test → generates bilingual Markdown table report → writes file + prints + appends GITHUB_STEP_SUMMARY; returns non-zero when more than half unreachable (for CI alerting) |
Zero external Python package dependencies, uses only the Python standard library:
| Standard library module | Purpose |
|---|---|
argparse |
Command-line argument parsing |
datetime |
Speed-test cache time judgment |
json |
Read/write mirror.json and cache files |
os / shutil
|
File/directory operations, cross-platform checks |
re |
Parse git progress output, URL parsing |
signal |
Process group signals (Linux process kill) |
socket |
TCP speed test, IPv4/IPv6 detection |
subprocess |
Call git, PowerShell (Windows PATH) |
sys |
Standard stream reconfiguration (Windows UTF-8), exit |
threading |
Speed monitor thread, thread lock |
time |
Speed-test timing, retry delay |
concurrent.futures |
Concurrent speed test (ThreadPoolExecutor) |
pathlib |
Path handling |
urllib.parse |
URL parsing |
ctypes (Windows) |
Call kernel32 for language, enable VT mode |
locale |
Unix language detection fallback |
System dependencies:
| Dependency | Version | Description |
|---|---|---|
| Python | 3.7+ | Uses from __future__ import annotations and type annotation syntax |
| Git | any | Must be in PATH, calls git clone --progress
|
fastclone.py
├── depends on → i18n.py (L, Lh, LANG)
├── reads → mirror.json (loads _CONFIG at startup)
└── reads/writes → speedcache/*.json (speed-test cache)
scripts/test_mirrors.py
└── reads → mirror.json (read-only, never modifies)
windows/fast-clone.cmd ──calls──> fastclone.py
windows/setup.bat ──calls──> fastclone.py --setup
linux/setup.sh ──creates wrapper──> fastclone.py
.github/workflows/mirror-test.yml ──calls──> scripts/test_mirrors.py
main()
├─ load_config() / get_mirrors() / get_default_mirror() ...
├─ parse_git_url() → _split_path() / _make_info() / _detect_platform()
├─ _resolve_mirror_list()
│ ├─ detect_ip_support() → _probe_ip_any() → _probe_ip()
│ ├─ filter_mirrors_by_ip()
│ └─ find_fastest_mirror() → _get_speed_results()
│ ├─ _find_valid_cache() (check cache)
│ └─ _tcp_latency() × N (concurrent live test) → _save_cache()
├─ prompt_delete_expired_caches() → _find_expired_caches()
└─ clone_with_fallback()
├─ apply_mirror() (4 strategies)
├─ clone_with_monitor()
│ ├─ SpeedMonitor.feed() / should_abort()
│ ├─ _stderr_reader() → _render_progress() / _parse_speed()
│ └─ _safe_kill() (abort)
├─ _set_remote() (reset after success)
└─ run_git() (fallback direct clone)
git clone https://github.com/bcggxx/fast-clone.gitKeep the repo in a fixed local location; do not move the directory after installation.
cd fast-clone\windows
setup.batThe install script checks Python/Git (PATH only) and adds fast-clone\windows\ to PATH. After that, fast-clone is available in any terminal.
cd fast-clone/linux
bash setup.shThe install script creates a wrapper pointing to the in-place fastclone.py (~/.local/bin user-level or /usr/local/bin system-level); mirror.json path stays the same.
| Dependency | Install |
|---|---|
| Python 3.7+ | python.org or package manager |
| Git | git-scm.com or package manager |
# Linux/macOS
python3 fastclone.py [args] [repo-url]
# Windows
python fastclone.py [args] [repo-url]# Default gh-proxy.org mirror
fast-clone https://github.com/user/repo
# Auto speed-test, pick fastest mirror
fast-clone --fastest https://github.com/user/repo
# Specify mirror
fast-clone --mirror github-akams https://github.com/user/repo
# Preview transformation, no clone
fast-clone -n https://github.com/user/repo
# List all mirrors
fast-clone -l
# Specify branch + shallow clone
fast-clone -b main --depth 1 https://github.com/user/repo
# Custom speed threshold
fast-clone --min-speed 2 --speed-timeout 120 https://github.com/user/repo# Force English
export FASTCLONE_LANG=en
# Force Chinese
export FASTCLONE_LANG=zhcd /path/to/fast-clone
git pull- Wrapper points to in-place
fastclone.py, takes effect without reinstall - Mirror list auto-syncs with
git pull -
speedcache/is gitignored, unaffected
| Field | Type | Default | Description |
|---|---|---|---|
default |
string | "gh-proxy-org" |
Default mirror key |
speed_threshold_kib |
int | 1024 |
Speed threshold (KiB/s), i.e., 1 MB/s |
speed_timeout_seconds |
int | 180 |
Timeout seconds for speed persistently below threshold |
connect_retries |
int | 3 |
Connection failure retry count per mirror |
mirrors |
object | — | Mirror dict, key is mirror identifier |
| Field | Required | Description |
|---|---|---|
name |
yes | Mirror display name |
platforms |
yes | Supported platforms list, e.g., ["github"], ["gitlab"]
|
transform |
yes | Transform strategy: prefix / domain_replace / path_prefix / domain_suffix
|
test_host |
yes | Hostname for speed test (TCP 443 connectivity test) |
ip |
no | Protocol filter: dual (default) / v4 / v6
|
description |
yes | Description text |
prefix |
required for prefix/path_prefix
|
Prefix URL |
old / new
|
required for domain_replace
|
Source/target for domain replacement |
old / suffix
|
required for domain_suffix
|
Source/suffix for domain suffix append |
| key | Mirror site | Platform | Strategy | IP |
|---|---|---|---|---|
gh-proxy-org * |
gh-proxy.org | github | prefix | dual |
gh-proxy-v4 |
v4.gh-proxy.org | github | prefix | v4 |
gh-proxy-v6 |
v6.gh-proxy.org | github | prefix | dual |
gh-proxy-cdn |
cdn.gh-proxy.org | github | prefix | v4 |
gh-proxy-com |
gh-proxy.com | github | prefix | dual |
ghproxy-net |
ghproxy.net | github | prefix | dual |
kkgithub |
kkgithub.com | github | domain_replace | dual |
github-akams |
github.akams.cn | github | prefix | dual |
gitclone |
gitclone.com | github | path_prefix | v4 |
github-ur1 |
github.ur1.fun | github | domain_replace | dual |
jihulab |
jihulab.com | gitlab | domain_replace | dual |
*is the default mirror.
Edit mirror.json, add an entry under mirrors:
"my-mirror": {
"name": "my-mirror.example.com",
"platforms": ["github"],
"transform": "domain_replace",
"old": "github.com",
"new": "my-mirror.example.com",
"test_host": "my-mirror.example.com",
"ip": "dual",
"description": "My self-hosted mirror"
}Delete an entry to disable it. Modify top-level "default" to switch the default mirror. Takes effect on next run.
.github/workflows/mirror-test.yml
-
Triggers: daily UTC 08:00 (Beijing 16:00) schedule +
workflow_dispatchmanual -
Permissions:
contents: write(create/delete Release and tag) -
Concurrency:
group: mirror-status,cancel-in-progress: false
- Checkout code
- Set up Python 3.13
- Run
python scripts/test_mirrors.py --timeout 5(continue-on-error: true, publishes report even when more than half unreachable) - Delete old
mirror-statusRelease + tag (--cleanup-tag) - Create new
mirror-statusRelease, body is full report, attachment ismirror-test-report.md
-
Fixed tag
mirror-status: overwritten daily, no history accumulation - Release body is full report: click to see each mirror's latency and reachability table
-
Bilingual table:
key | mirror | ip | latency | status -
Read-only: does not modify
mirror.json, mirror changes only by manual edit
User runs fast-clone <url>
│
▼
main() parses args
│
▼
parse_git_url() parses URL
│
▼
Platform known? ──no──→ warn + direct clone
│yes
▼
_resolve_mirror_list() resolves mirror candidates
├─ detect_ip_support() IP detection
├─ filter_mirrors_by_ip() IP filter
└─ --fastest? ──yes──→ find_fastest_mirror() speed-test sort
│
▼
Print repo info
│
▼
--dry-run? ──yes──→ preview URL transform, exit
│no
▼
prompt_delete_expired_caches() ask to clean cache
│
▼
clone_with_fallback() ◄────────────────┐
│ │
▼ │
Iterate mirror list (for mk in mirror_keys) │
│ │
▼ │
apply_mirror() generate mirror URL │
│ │
▼ │
clone_with_monitor() single clone │
├─ start git clone --progress │
├─ SpeedMonitor monitors speed │
└─ _stderr_reader renders progress │
│ │
▼ │
┌────┴────┐ │
│ status? │ │
└────┬────┘ │
┌────┼────┬────────┐ │
▼ ▼ ▼ ▼ │
ok speed conn other │
│ _timeout error _error │
│ │ │ │ │
│ │ │ └──→ switch next ─────┤
│ │ │ │
│ │ ▼ │
│ │ retry-- >0? ─yes──→ retry ───────┤
│ │ │no │
│ │ └──→ switch next ─────────────┤
│ │ │
│ └──→ switch next ───────────────────┤
│ │
▼ │
_set_remote() reset origin to official │
│ │
▼ │
return 0 (success) │
│
Mirror list exhausted ──→ fallback direct clone ──┘
--fastest triggers find_fastest_mirror()
│
▼
Filter candidates by platform
│
▼
detect_ip_support() + filter_mirrors_by_ip()
│
▼
_get_speed_results()
│
▼
Check _find_valid_cache(platform)? ──hit──→ reuse cached results
│miss
▼
Concurrent _tcp_latency() × N (ThreadPoolExecutor, max 10)
│
▼
_save_cache() write speedcache/YYYY-MM-DD_HHMMSS.json
│
▼
Pick lowest-latency mirror, fall back to default if all unreachable
| Mechanism | Implementation | Location |
|---|---|---|
| Auto remote reset | After successful clone, uses git remote set-url origin <official> to swap mirror address back to official, so subsequent pull/push go through official repo |
_set_remote() |
| SSH address handling | SSH addresses clone via mirror as HTTPS, remote set back to original SSH after completion |
parse_git_url() + _set_remote()
|
| Refuse clone to current dir | After resolving target dir, checks if equals Path.cwd(), die() if so |
clone_with_fallback() |
| Refuse to delete current dir | When cleaning leftovers, checks tp.resolve() != Path.cwd().resolve()
|
clone_with_fallback() |
| Low-speed protection | Speed persistently < threshold beyond timeout → abort, clean leftovers, switch mirror |
SpeedMonitor + clone_with_monitor()
|
| Connection retry | Each mirror retries N times on connection failure, switches when exhausted | clone_with_fallback() |
| Direct-connect fallback | After all mirrors fail, finally attempts official repo directly | end of clone_with_fallback()
|
| Design | Description |
|---|---|
| Config missing fallback | When mirror.json missing/corrupt, falls back to empty-mirror config, tool still direct-clones |
| IP detection dual endpoints | Cloudflare first, falls back to Tencent DNSPod, avoids false-negative when Cloudflare blocked |
| IP detection all-fail no filter | When both stacks fail, no IP-version filtering, lets real clone decide |
| Cache corruption tolerance | Cache file JSON parse failure treated as expired, doesn't affect operation |
| Cross-platform process management | Windows uses taskkill /T, Linux uses process group SIGTERM→SIGKILL
|
| Windows UTF-8 forced |
sys.stdout.reconfigure(encoding='utf-8') at startup solves Chinese garbling |
| Windows VT mode |
Color.enable() calls SetConsoleMode to enable ANSI colors |
| PATH injection dedup |
_add_to_path() compares by trailing-slash-stripped lowercase, avoids duplicate accumulation on reinstall |
| PATH injection avoids truncation | Uses PowerShell SetEnvironmentVariable instead of setx (avoids 1024-char truncation) |
| Speed-test cache isolation |
speedcache/ is gitignored, git pull doesn't touch local cache |
| CI read-only |
test_mirrors.py never modifies mirror.json, mirror changes only manual |
| Design | Description |
|---|---|
| Concurrent speed test |
ThreadPoolExecutor concurrent TCP speed test, up to 10 parallel |
| Speed-test cache | Same-platform speed-test results reused within 7 days, avoids repeated tests |
| In-process IP cache |
detect_ip_support() result cached to _IP_SUPPORT, detected only once per run |
| Module-level config cache |
_CONFIG loaded once at startup, avoids repeated file reads |
| Daemon thread | stderr reader thread is daemon, auto-ends when main process exits |
This Code Wiki is generated based on the fast-clone repository source code, covering project architecture, module responsibilities, key classes and functions, dependencies, how to run, configuration reference, CI automation, and design highlights. For the latest implementation, please read the source files directly.