CLI reference
tama copies list
Reports dependency checkouts, bare Git caches and other second repository copies under the roots you name, with their canonical checkout, apparent size and removal refusals. It mutates nothing.
#Invocation
tama copies list [--root <PATH>]... [--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 |
--json | Prints the exact document below instead of the text report. Both modes read the same state |
#What counts as a copy
The named roots and their direct child repositories are canonical. Deeper
repositories are candidates, including ignored paths, package-manager
checkouts, repositories, node_modules,
Pods and build directories. Only Git's own .git
metadata is pruned; a directory name never hides a dependency checkout.
A .git file does not by itself mean a linked worktree:
submodules also use one. Git's private and common metadata directories
distinguish them. Linked worktrees appear once under linkedWorktrees
and belong to tama worktrees list;
submodule checkouts remain copy candidates. Bare repositories are recognized
by Git and have no working-tree changes, but their history is still checked.
The pass checks working-tree changes, the origin, and every
stored Git object, including dangling commits and annotated tags. Remote-tracking
refs remain a local record, not a network check. When they do not retain the
history, Tama looks for a canonical checkout whose refs reach every stored object.
That checkout must use its own object storage, without alternates or symlinks.
The result is named in historyRetainedBy; null means
no such proof was established. Missing objects or failed history reads do not
become a clean result. Git's lazy fetching is disabled.
Ownership still follows origins, not merely shared history. A retention
proof does not claim that two differently named repositories are the same.
An owner-less candidate still needs an explicit --only claim.
Canonical checkouts from every named root contribute, regardless of root order.
An initialized repository without commits is readable. Damaged metadata
never borrows the enclosing repository's history: Git must identify the
candidate itself, or the pass reports it under unreadable.
#Output document
Under --json, exactly these fields, with the schema version the whole
copies contract is fixed at:
{"schemaVersion":Number,"roots":[String],"copies":[{"path":String,"owner":String|null,"origin":String|null,"bytes":Number,"dirty":Boolean,"unpublished":Boolean,"historyRetainedBy":String|null,"ownsWorktrees":Boolean|null,"named":Boolean}],"copyCount":Number,"bytes":Number,"linkedWorktrees":[String],"twins":[{"path":String,"twin":String,"origin":String,"basis":"origin"|"first-commits"}],"unreadable":[String],"unreadableDirectories":[{"path":String,"reason":String}]}
roots echoes the roots you passed, resolved. owner is the canonical
checkout of the same repository under those roots, or null when this
directory is the only checkout of its repository here — which is a refusal on
remove until you name it with --only.
bytes is apparent size, per copy and summed for the pass. named is
false here and only ever true in a narrowed removal document.
twins carries two checkouts of one repository that the pass reports and
removes neither of. With basis origin, both sit directly under a root
and name the same origin, and origin is that origin. With basis
first-commits, their origins differ but their histories start from the
same commits, and origin holds those commits. That is what a repository
GitHub moved looks like: every checkout cloned before the move keeps the old
address. A fork starts from the same commits too, which is why this match only
reports and never makes one checkout the owner of the other. The text report
prints it as twin history: <path> and <path> start from the same
commits (<commits>) under different origins, …. unreadable names every
candidate repository git then refused to answer for:
reported rather than dropped, because silence would be the pass claiming it
read a checkout it never read.
unreadableDirectories names every directory or entry the walk
could not read, each with the operating system's reason. A pass
carrying one of these cannot say the roots hold one checkout per repository,
only that it saw no second one where it could look, so it exits 1 and
prints could not read <path>: <reason> to stderr in both output
modes. A checkout whose .git/worktrees records exist and cannot be
read is listed here too, with the read error as its reason, instead of being
treated as owning no worktree; a copy whose own records cannot be read carries
ownsWorktrees: null and the error in its inspection, which
remove refuses as unreadable Git state.
The same document is the answer of tama request copies/list with body
{"roots":[String]}, the operation Tama Desktop runs
(tama request).
#State effect
None. The command walks directories and runs read-only Git status, ref,
object and origin queries. It does not fetch missing objects, and it has no
--apply, no --force, no --except and no --only: those belong
to tama copies remove, which is the only leaf
with anything to spare, narrow or delete.
#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.
A root that cannot be read is reported in
unreadableDirectories with the operating system's own reason, and the
command exits 1 rather than reporting a clean tree it never walked.
A failed history query is an operational error and names the actual failed read.
Safety refusals belong to tama copies remove.
Return to the copies command group or the complete CLI tree.