xgit simple git

xgit

this project — static git page generator by polymath

kb.hax.al
clone git clone https://kb.hax.al/xgit

commit cd2f58e8772c30d47b044e258a58ceed4ec992a6
author: polymath <polymath@localhost>
date: 2026-08-26 12:09
parents: d04929cf

Publish this project itself as the xgit repository.

Support path:. mirrors so the site can browse its own source tree.
MREADME.md+16-1
Mconfig.toml+6-0
Mxgit/fetch.py+27-0
diff --git a/README.md b/README.md
index 1599d19..7cd72e8 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,22 @@ owner = "polymath"
 
 Needs your SSH agent / keys available to the machine running xgit.
 
-### Local path mirrors
+### Local filesystem checkout (`path:`)
+
+Publish this xgit project (or any other checkout on disk):
+
+```toml
+[[repos]]
+name = "xgit"
+url = "path:."
+description = "this project — static git page generator by polymath"
+owner = "polymath"
+```
+
+`path:.` is relative to the directory that contains `config.toml`.  
+Absolute paths work too: `path:/home/you/code/myapp`.
+
+### Local seed demos
 
 ```toml
 [[repos]]
diff --git a/config.toml b/config.toml
index aeb7ada..6274c8e 100644
--- a/config.toml
+++ b/config.toml
@@ -19,6 +19,12 @@ max_diff_bytes = 1048576
 
 # ── Demo repositories (local, created by ./demo or ./sync) ─────────────
 
+[[repos]]
+name = "xgit"
+url = "path:."
+description = "this project — static git page generator by polymath"
+owner = "polymath"
+
 [[repos]]
 name = "hello"
 url = "local:hello"
diff --git a/xgit/fetch.py b/xgit/fetch.py
index 15c0abf..95bb2bb 100644
--- a/xgit/fetch.py
+++ b/xgit/fetch.py
@@ -18,6 +18,7 @@ def fetch_all(cfg: Config) -> None:
 
 def fetch_one(cfg: Config, repo: RepoConfig) -> Path:
     dest = cfg.repo_path(repo.name)
+
     if repo.url.startswith("local:"):
         local_name = repo.url.split(":", 1)[1]
         seed_dir = cfg.root / "data" / "seed" / local_name
@@ -34,6 +35,19 @@ def fetch_one(cfg: Config, repo: RepoConfig) -> Path:
             gitutil.set_description(dest, repo.description)
         return dest
 
+    path_src = _local_git_path(cfg, repo.url)
+    if path_src is not None:
+        if not path_src.exists() or not gitutil.is_git_repo(path_src):
+            raise gitutil.GitError(f"path is not a git repository: {path_src}")
+        # Never mirror a repo into itself
+        if path_src.resolve() == dest.resolve():
+            raise gitutil.GitError(f"refusing to mirror {path_src} onto itself")
+        print(f"  mirroring {path_src}")
+        _mirror_from_workdir(path_src, dest)
+        if repo.description:
+            gitutil.set_description(dest, repo.description)
+        return dest
+
     if dest.exists() and gitutil.is_git_repo(dest):
         gitutil.fetch_all(dest)
     else:
@@ -47,6 +61,19 @@ def fetch_one(cfg: Config, repo: RepoConfig) -> Path:
     return dest
 
 
+def _local_git_path(cfg: Config, url: str) -> Path | None:
+    """Resolve path:. / file:// URLs to a filesystem git checkout."""
+    if url.startswith(("local:", "git@", "http://", "https://", "git://", "ssh://")):
+        return None
+    if url.startswith("path:"):
+        raw = url.split(":", 1)[1].strip() or "."
+        p = Path(raw).expanduser()
+        return p.resolve() if p.is_absolute() else (cfg.root / p).resolve()
+    if url.startswith("file://"):
+        return Path(url[7:]).expanduser().resolve()
+    return None
+
+
 def _mirror_from_workdir(workdir: Path, dest: Path) -> None:
     """Create/update a bare mirror from a local non-bare repo."""
     if dest.exists():