RepoForge
RepoForge provides repository archive discovery and safe refresh helpers used by
thesa.
Made by Trevor Knott for Knott Dynamics. RepoForge is part of the thesa archive
stack alongside modelforge, siteforge, Scrin, and Aisling.
The crate is intentionally focused. It scans an archive root for Git worktrees,
derives stable owner/repo slugs from GitHub remotes or archive paths, and
refreshes clones with git pull --ff-only without touching dirty worktrees.
RepoForge is a library crate, not a full CLI. It gives applications the safe Git operations and deterministic archive identifiers they need while leaving UI, manifest writing, and policy decisions to the caller.
What It Does
- Finds Git worktrees recursively under an archive root.
- Converts GitHub remotes into stable
owner/reposlugs. - Falls back to archive path slugs when remotes are missing or non-GitHub.
- Restricts discovery to one GitHub owner/user/org when requested.
- Filters discovered archives by slug or path text.
- Refuses dirty worktrees before refresh.
- Runs
git pull --ff-onlyfor clean worktrees. - Returns structured updated, unchanged, and failed outcomes.
What It Does Not Do
- Does not delete repositories.
- Does not reset, checkout, rebase, merge, stash, or force-pull.
- Does not write archive manifests or checksums.
- Does not assume a terminal UI.
- Does not require GitHub API credentials.
Install
[]
= "0.1"
The package and library crate name are both repoforge.
Archive Layout
RepoForge is designed for archive trees like this:
archives/
octocat/
Hello-World/
.git/
rust-lang/
rust/
.git/
When remote.origin.url is a GitHub URL, the slug is derived from the remote.
For example https://github.com/octocat/Hello-World.git becomes
octocat/Hello-World.
If there is no GitHub remote, RepoForge falls back to the path layout. For
example archives/octocat/Hello-World becomes octocat/Hello-World.
Discovery
use Path;
let archives = discover_git_archives?;
for archive in archives
# Ok::
Use a filter to limit results by slug or path:
let archives = discover_git_archives?;
# Ok::
Discovery is recursive but stops descending when it finds a Git worktree.
.git, .thesa, and target directories are skipped.
Discover only one GitHub owner/user/org:
let archives = discover_git_archives_for_owner?;
assert!;
# Ok::
Owner matching is case-insensitive and uses the derived owner/repo slug. This
works for GitHub remote-derived slugs and archive layouts like
archives/<owner>/<repo>.
API Surface
discover_git_archives(root, filter): find archive worktrees.discover_git_archives_for_owner(root, owner, filter): find worktrees for one GitHub owner/user/org.refresh_git_archives(archives, concurrency): refresh many worktrees.refresh_one_git_archive(archive): refresh one worktree.parse_github_remote_slug(remote_url): parse GitHub remotes intoowner/repo.split_owner_repo_slug(slug): borrow validatedownerandrepoparts from anowner/reposlug.fallback_archive_slug(root, repo_path): derive a slug from archive layout.ensure_clean_git_worktree(archive): fail if local changes exist.git_head(archive): read the currentHEADhash.run_git_capture(repo_path, args): run low-level Git commands with RepoForge errors.
Core data types:
GitArchiveCandidate: discovered worktree path, slug, and optional remote.GitArchiveCandidate::slug_parts(): borrow validated(owner, repo)slug parts.GitArchiveCandidate::owner(): borrow the slug owner.GitArchiveCandidate::repo_name(): borrow the slug repository name.GitArchiveCandidate::matches_owner(owner): case-insensitive owner match.GitRefreshSummary: aggregate counts, successful entries, and failures.GitRefreshStatus:UpdatedorUnchanged.RepoForgeError: typed errors for non-directory roots, missing Git, dirty trees, Git command failures, and I/O.
Refresh
let archives = discover_git_archives?;
let summary = refresh_git_archives;
println!;
if summary.has_failures
# Ok::
Refresh behavior:
- Runs
git status --porcelainbefore pulling. - Dirty worktrees fail fast and are not pulled.
- Runs
git pull --ff-onlyfor clean worktrees. - Reports
UpdatedwhenHEADchanges. - Reports
Unchangedwhen the pull succeeds without changingHEAD. - Uses bounded concurrency with
refresh_git_archives.
Slug Helpers
assert_eq!;
Supported GitHub remote forms include HTTPS and SSH:
https://github.com/owner/repo.githttp://github.com/owner/repo.gitgit@github.com:owner/repo.gitssh://git@github.com/owner/repo.git
Safety Contract
RepoForge does not delete, reset, checkout, stash, or force-pull anything. It
only uses read-only Git inspection commands plus git pull --ff-only after
confirming the worktree is clean.
Callers such as thesa remain responsible for archive metadata sidecars,
checksums, and higher-level reporting.
Package Contents
Cargo releases are explicitly whitelisted to avoid shipping sessions, notes, build output, or local archives. The package contains only:
Cargo.tomlCargo.lockREADME.mdLICENSECHANGELOG.mdsrc/**
Role In Thesa
thesa uses RepoForge for Git archive discovery, GitHub slugging, dirty-tree
protection, and fast-forward-only refreshes. thesa then wraps those outcomes
with Scrin/Aisling terminal reporting and .thesa integrity manifests.