CLI reference
tama copies remove
Removes every repository copy under the roots you name, previewing the whole pass unless --apply is given, and refusing the entire pass before deleting anything when a selected copy carries work that exists nowhere else.
#Invocation
tama copies remove [--root <PATH>]... [--except <PATH>]... [--only <PATH>]... [--apply] [--force] [--json]
#Inputs and options
| Option | Meaning |
|---|---|
--root <PATH> | Repeatable, and required at least once. Each value is a directory whose checkouts are walked. There is deliberately no default root |
--except <PATH> | Repeatable. The path of one copy to leave completely alone: neither removed, nor counted as removable, nor able to refuse the pass |
--only <PATH> | Repeatable. Walks only the named copy subtrees and the canonical repository headers needed to identify their owners. Accepts each named copy as removable even when it is the sole checkout of its repository here. Not combinable with --except |
--apply | Performs the removal. Without it the command previews and mutates nothing |
--force | Overrides copy safety refusals, including uncommitted changes and unpublished history. It never overrides an incomplete directory walk, an unknown selection, or a filesystem deletion error |
--json | Prints the exact document below instead of the text report, in both preview and applied mode |
#What is proved before a directory is deleted
A copy goes by deleting its directory, so the pass asks git first, and each answer is a refusal with its own sentence:
<path> has uncommitted changes; commit them there, then run the pass again.
<path> holds Git history without a remote-tracking or canonical retention proof; retain that history before removing this directory.
<path> has no origin remote, so this pass cannot prove its history exists anywhere else.
<path> is the only checkout of its repository under the roots given, so it is not a copy of anything.
<path> owns linked worktrees; remove those first with 'tama worktrees remove --root <PATH> --apply'.
historyRetainedBy names a canonical checkout whose independent,
referenced Git objects retain every stored object in the candidate. This
protects dangling commits and annotated tags, not only current branches.
A proven candidate does not need --force merely because its
remote-tracking refs or origin are absent. A missing owner still requires
--only; dirty files and owned worktrees remain refusals.
A repository with a failed working-tree or history read remains in the inventory.
inspectionError records the failed Git command and its error;
dirty or unpublished is null when that observation failed.
Normal removal refuses it with unreadable-git-state:
<path> has unreadable Git state; inspect the reported Git error before forcing removal.
An explicit --force overrides this safety refusal, including for
a checkout whose alternate object store has disappeared. It does not override
failure to identify the directory as a repository or filesystem errors.
The pass is all-or-nothing: with any refusal outstanding an --apply
removes none of the copies it selected, so Tama never deletes the first
three and then discovers the fourth carries work. Immediately before each
deletion the filesystem and Git state are checked again. A changed origin,
dirty working tree, newly owned worktrees or lost retention proof stops
further deletion. A symlink, a path outside the named roots or an unreadable
Git state is an operational failure rather than a deletion.
When selected copies contain other selected copies, descendants are removed
before their parents. Shared Git checkouts are removed before selected stores
that supply their alternate objects, including SwiftPM bare caches. Tama plans
this order before deleting anything; cyclic dependencies refuse the pass with
selected copies have cyclic directory or Git object-store dependencies; nothing was removed.
Filesystem errors during deletion are operational failures, not rollback
guarantees. Run copies list again to inspect the remaining state.
#Sparing with --except, claiming with --only
--except is the operator's exclusion: an excepted copy is dropped from
the pass before anything judges it, so a dirty copy cannot refuse a pass it
is not part of. --only is the opposite move: it narrows the pass to the
paths it names and answers the one question the pass cannot answer for
itself — whether losing a directory that is the sole checkout of its
repository here loses anything the operator wants. It answers nothing else:
a claimed copy with dirty files or unretained history is still refused.
They are two answers to the same question, so a command carrying both is a usage error, and a value of either that matches no copy the pass found is one too: a pass the operator believed narrowed must not quietly become a pass over everything.
#What a named removal reads
With --only, Tama inspects Git headers at each root and its direct
children and, when needed, their referenced objects and object storage to prove
retention. It walks only the named copy subtrees, not unrelated working trees. An unreadable cloud folder
elsewhere under a broad root cannot prevent removal of an explicitly named,
fully readable copy. A read failure in a selected subtree or in the required
canonical discovery still stops the whole pass, including with
--force.
For example, with a canonical checkout at /workspace/repo and its
dependency copy at /workspace/repo/vendor/copy, preview and then
remove just that copy:
tama copies remove --root /workspace --only /workspace/repo/vendor/copy --json
tama copies remove --root /workspace --only /workspace/repo/vendor/copy --apply --json
These are example paths; use existing directories on your machine.
copies list and removal without --only still walk the
entire named roots. A successful named removal proves nothing about copies
outside its selected subtrees.
#Preventing Swift dependency copies from returning
Removal does not change the builder or editor that created a copy. Use
stado product swift --package-path PATH build for canonical
Swift builds. For a SourceKit workspace, stado product swift
--package-path PATH index compiles the canonical dependency graph and
writes a BSP connection plus SourceKit configuration. The editor reads actual
compiler arguments and the shared index store; it does not resolve packages.
Keep .bsp/wisent-native.json and
.sourcekit-lsp/config.json with the workspace. If the generated
compiler cache disappears, the provider reports the failed read and the
index command needed to rebuild it. It does not fall back to
cloning. A partial product build is refused for indexing, as is an existing
unrelated build-server declaration. Existing editor sessions keep their
loaded build system; the command does not restart them.
--editor-workspace DIR permits an isolated editor qualification
workspace. sourcekit-lsp debug index --project DIR exercises that
provider, but an indexing pass alone does not prove navigation, missing-cache
recovery, or that an already running editor has changed providers. Verify the
applicable editor behavior before deleting copies it may still use.
#Output document
Under --json, the list document plus four
fields, with the same schema version:
{... list fields ...,"excepted":[String],"applied":Boolean,"removed":[String],"refused":[{"path":String,"reason":String}]}
excepted echoes what --except held back, so the document states what
the pass deliberately left alone; those copies appear in neither
removed nor refused, in a preview and in an applied pass alike.
applied is true only for an --apply pass that was free of refusals.
reason identifies the safety refusal. --force empties
refused, but does not turn an incomplete walk or failed deletion
into success.
unreadableDirectories reports failures in this request's required
walk: the full roots for an unrestricted pass, or canonical discovery and
selected subtrees with --only. An all-or-nothing removal cannot be
all-or-nothing over a selected tree it could not see, so a pass carrying one of these
answers "applied":false, "removed":[], prints
could not read <path>: <reason> followed by this pass could
not read N directories under the roots, so it cannot say what they hold;
nothing was removed, and exits 1 — in a preview as well as under
--apply.
The same document is the answer of tama request copies/remove with body
{"roots":[String],"except":[String],"only":[String],"apply":Boolean,"force":Boolean}
(tama request), where a body carrying both
except and only, or naming an unknown selection, is refused
with status 400. A canonical checkout is not a removable copy, even with
force. Operational failures answer status 500; safety
refusals remain status 200 documents with applied:false and
the actual refusal details. This operation is what the
Copies screen runs.
#Refusal boundary
Passing no --root is a usage error, exit code two:
tama copies needs at least one --root; a machine-wide default would walk directories Tama does not own.
So is a value that names nothing this pass found:
tama copies does not know the copy <path>; --only takes the path of a copy this pass found.
tama copies does not know the copy <path>; --except takes the path of a copy this pass would otherwise remove.
A refused --apply exits one and prints every refusal to stderr; under
--json it exits one with "applied":false and "removed":[]. A
failed deletion is an operational failure, exit one. A complete preview
exits zero even when it reports safety refusals. An applied pass exits zero
when it removes everything selected, including an empty selection.
Return to the copies command group or the complete CLI tree.