# RepoForge
RepoForge provides repository archive discovery and safe refresh helpers used by
`thesa`.
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.
## Install
```toml
[dependencies]
repoforge = "0.1"
```
The package and library crate name are both `repoforge`.
## Archive Layout
RepoForge is designed for archive trees like this:
```text
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
```rust
use std::path::Path;
let archives = repoforge::discover_git_archives(Path::new("./archives"), None)?;
for archive in archives {
println!("{} -> {}", archive.slug, archive.path.display());
}
# Ok::<(), repoforge::RepoForgeError>(())
```
Use a filter to limit results by slug or path:
```rust
let archives = repoforge::discover_git_archives(
std::path::Path::new("./archives"),
Some("octocat"),
)?;
# Ok::<(), repoforge::RepoForgeError>(())
```
Discovery is recursive but stops descending when it finds a Git worktree.
`.git`, `.thesa`, and `target` directories are skipped.
## Refresh
```rust,no_run
let archives = repoforge::discover_git_archives(std::path::Path::new("./archives"), None)?;
let summary = repoforge::refresh_git_archives(&archives, 4);
println!(
"updated={} unchanged={} failed={}",
summary.updated,
summary.unchanged,
summary.failed.len()
);
if summary.has_failures() {
for failure in &summary.failed {
eprintln!("{}: {}", failure.slug, failure.message);
}
}
# Ok::<(), repoforge::RepoForgeError>(())
```
Refresh behavior:
- Runs `git status --porcelain` before pulling.
- Dirty worktrees fail fast and are not pulled.
- Runs `git pull --ff-only` for clean worktrees.
- Reports `Updated` when `HEAD` changes.
- Reports `Unchanged` when the pull succeeds without changing `HEAD`.
- Uses bounded concurrency with `refresh_git_archives`.
## Slug Helpers
```rust
assert_eq!(
repoforge::parse_github_remote_slug("git@github.com:octocat/Hello-World.git"),
Some("octocat/Hello-World".to_string())
);
```
Supported GitHub remote forms include HTTPS and SSH:
- `https://github.com/owner/repo.git`
- `http://github.com/owner/repo.git`
- `git@github.com:owner/repo.git`
- `ssh://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.