Files

Subtree Publish

This tool publishes selected monorepo subtrees to standalone Git mirror repos. It replaces the useful part of submodules: an externally addressable Git repo for a package/schema/helper, without keeping submodules in the working tree.

Run manually:

nix run ./infra/subtree-publish -- publish

Install the local pre-push hook:

nix run ./infra/subtree-publish#install-hook

The hook calls the same publisher before a Git push. Disable it for one push with:

QUIXOS_SUBTREE_PUBLISH=0 git push

Manifest Format

The manifest lives at infra/subtree-publish/manifest.json.

{
  "version": 1,
  "exports": [
    {
      "name": "quixos-nix-helpers",
      "enabled": true,
      "path": "quixos-instance/quixos-nix-helpers",
      "remote": "https://example.invalid/quixos-nix-helpers.git",
      "ref": "refs/heads/exported"
    }
  ]
}

Fields:

  • version: manifest schema version. Currently only 1.
  • exports: list of subtree mirrors.
  • name: stable local export ID. Used for cache keys, temp refs, and logs.
  • enabled: optional boolean. Only true entries are published. Missing or false means the entry is documentation/planning only.
  • path: subtree path relative to the monorepo root.
  • remote: Git remote URL for the standalone mirror repo.
  • ref: remote ref to push, usually refs/heads/exported.

Agents should edit manifest.json directly when adding or enabling exports. Keep mirror repos read-only from the outside; normal edits should happen in the monorepo and flow outward through this publisher.

Idempotence

Each exported repo gets a tracked metadata file:

.quixos-subtree-source.json

It records:

  • source repo URL
  • source commit
  • source path
  • export name

The local monorepo keeps an ignored cache at:

.var/subtree-publish/cache.json

For each export, the cache stores checkedThroughCommit. If a later push has no changes under that export path, the cache advances without publishing. The tracked sourceCommit in the mirror advances only when mirror content actually changes.

If history is rewritten and the cached commit is no longer an ancestor of the new pushed commit, the cache is ignored and the publisher checks from the last published source commit if available.