sow repo

列出、创建、查看与删除仓库 —— 锁、事务与 Generation 的边界。

一个仓库(Repository)独占一份 pool/、一份 dists/、一个 SQLite 数据库与一个私有状态目录。它是 锁、事务恢复、Generation 编号与 Changeset 的边界——跨仓库不去重,也不承诺跨仓库原子提交。 sow repo 管理的就是这条边界。

语法

sow repo ls [-C DIR] [--json]
sow repo new NAME [-C DIR] [-T DUR | -N] [--json]
sow repo show [NAME] [-C DIR] [-r NAME] [--json]
sow repo migrate [NAME] [--abort] [-j N] [-C DIR] [-r NAME] [-T DUR | -N] [--json]
sow repo rm NAME [-f|--force] [-C DIR] [-T DUR | -N] [--json]

命名

仓库名必须匹配 [a-z0-9][a-z0-9._-]*,且不能是 ....sowpooldists,也不能与工作区 保留文件冲突。

sow repo new .sow
operation rejected: managed: operation rejected: name ".sow" must match [a-z0-9][a-z0-9._-]*

路径不可指定。仓库永远位于 <workspace>/<NAME>/

sow repo ls

只读列出工作区里的全部仓库。

sow repo ls
NAME	PROTECTED	DISTS	GENERATION	STATUS	PACKAGES	MEMBERSHIPS
infra	true	1	1	clean	0	0
pgsql	false	2	2	clean	0	0
参数说明默认
-C, --workdir DIR工作区发现的起始目录当前目录
--json输出版本化 JSON envelopefalse

STATUS 取值为 cleandirtyrecoveringerror。各状态对客户端意味着什么,见 事务与恢复

sow repo new

原子更新 sow.yml,然后创建 <workspace>/<NAME>/{pool,dists}、SQLite 数据库与私有状态目录。新仓库 处于 Generation 0、clean 状态。

sow repo new pigsty
created pigsty: path=/srv/repo/pigsty protected=false dists=0 generation=0 status=clean packages=0 memberships=0
参数说明默认
-C, --workdir DIR工作区发现的起始目录当前目录
-T, --timeout DUR等待锁的最长时间;0 无限等待0
-N, --no-wait锁被占用时立即失败false
--json输出版本化 JSON envelopefalse

repo new 取的是工作区锁而不是仓库锁——此时仓库数据库还不存在。它不接受 -r,位置参数已经指明了 目标。

对已存在的仓库再跑一次是收敛型 no-op,只报告当前状态,因此在 provisioning 脚本里是安全的。

sow repo show

只读显示一个仓库的细节。省略 NAME 时按 CLI 全局约定 中的仓库选择规则 解析。

sow repo show pigsty
repository pigsty:
  path: /srv/repo/pigsty
  protected: false
  dists: 2
  generation: 6
  desired_revision: 6
  status: clean
  packages: 5
  memberships: 8
  config: {"protected":false,"signing":{"rpm":{"packages":{"mode":"never"}}},"dists":{"el9":{"format":"rpm","architectures":["x86_64","aarch64"],"limit":1,"exclude":[{"kind":["debuginfo","debugsource"]}]},"trixie":{"format":"deb","architectures":["x86_64","aarch64"],"limit":0,"exclude":null}}}
  dirty_reasons: []
  recent_operation: id=4142220455201181493 kind=add state=done error_class= created_at=2026-08-04T04:09:24.995538Z updated_at=2026-08-04T04:09:25.332772Z
参数说明默认
-C, --workdir DIR工作区发现的起始目录当前目录
-r, --repo NAME省略 NAME 时用它选择仓库按选择规则
--json输出版本化 JSON envelopefalse

同时给出 NAME-r 时两者必须一致;不一致会在读取任何状态前失败:

sow repo show demo -r empty
operation rejected: repo show NAME "demo" and --repo "empty" select different repositories

sow repo migrate

把未发布 C2 原型创建的仓库从视图级 RPM alias 迁移到 v0.2.0 单包体布局。它会改写 RPM 元数据使其访问根 pool/,记录耐久 transition,推进 Generation,并把 schema: sow/v2 改为 schema: sow/v3

sow repo migrate pigsty

操作可恢复。耐久 commit intent 之前,--abort 会放弃 staged transition,保持 live 仓库不变。 commit intent 之后只能前滚;重复运行 repo migrate,直到宽限期与清理条件全部完成。

参数含义默认值
-j, --jobs N并行校验/渲染 worker逻辑 CPU 数
--abort放弃提交前迁移false
-C, --workdir DIR工作区发现起点当前目录
-r, --repo NAME省略 NAME 时选择仓库选择规则
-T, --timeout DUR最长锁等待;0 无限等待0
-N, --no-wait锁被占用时立即失败false
--json输出版本化 JSON envelopefalse

新建 v0.2.0 工作区已经使用单包体布局,不需要迁移。

sow repo rm

删除一个仓库:它在 sow.yml 中的条目、数据库、pool/dists/ 与私有状态。绝不跟随符号链接, 也绝不越出固定的仓库路径。

不加 -f 时,只能删除空仓库——没有 Dist、没有 Membership、没有 Package Object:

sow repo rm infra
removed repository infra
sow repo rm pgsql
operation rejected: managed: operation rejected: repository "pgsql" is not empty; use --force
sow repo rm pgsql -f
removed repository pgsql
参数说明默认
-f, --force删除非空的、未 protected 的仓库false
-C, --workdir DIR工作区发现的起始目录当前目录
-T, --timeout DUR等待锁的最长时间;0 无限等待0
-N, --no-wait锁被占用时立即失败false
--json输出版本化 JSON envelopefalse

-f 到底降级了什么

-f 只放宽为空这一个前置条件。它不绕过路径安全检查、不绕过符号链接拒绝、也不绕过 protected 门禁。

protected

sow.yml 中的 protected: true 直接封死仓库删除,加 -f 也不行:

sow repo rm alpha -f
operation rejected: managed: operation rejected: repository "alpha" is protected

要删除受保护的仓库,必须先改 sow.yml,通过 sow config check,再重试。没有 --yes,也没有临时覆盖开关。

protected 只作用于仓库删除。受保护仓库上的包级操作不受影响——addrmbuild,乃至 dist rm 都照常工作:

sow dist rm el9 -r alpha -f
removed dist el9 from alpha

示例

为两层结构创建仓库:

sow repo new infra
sow repo new pgsql

在 cron 任务中快速失败,而不是排队等另一个写者:

sow repo new nightly -N || echo "另一个写者持有工作区锁"

一行一个仓库地做审计:

sow repo ls --json | jq -r '.result.repositories[] | "\(.name)\t\(.status)\tgen=\(.generation)"'

退出码

触发条件
0列出、创建、显示或删除成功;或 repo new 收敛了已存在的仓库
1创建或删除目录树时的运行时 I/O 错误
2用法错误、工作区未找到,或仓库选择有歧义
4工作区锁被占用,且给了 --no-wait--timeout 到期
5工作区 journal 的完整性或恢复错误
6名称非法、仓库不存在、非空但未给 -fprotected,或 NAME-r 冲突

参见

最后修改:2026-08-08: docs: align site with SOW v0.2.0 (9c98f8c)