Client commands
Documentation/Native client workflows
Client commands44 min read

Client workflows

On this page

For every command, option, default, and exit code, see the client CLI reference. This guide explains how to use those commands together and what happens to your working files.

Use scribe -h for commands grouped by workflow and common options, then scribe COMMAND --help for a command's complete options and subcommands.

All command-line tools support global --json; see JSON output for automation for results, errors, and exit codes.

New here? Start with the minimal-server tutorial, then follow the deployment series as your setup grows.

text
scribe [global options] <command> [options] [paths]

Commands that operate on a workspace find it by walking up from the current directory. Paths may be relative to the current directory or absolute; they must lie inside the workspace.

Choose a collaboration workflow

TaskStart here
Download source without all art assetsViews and profiles
Assemble independently versioned game, engine, and art repositoriesRepository mounts
Publish parent and child work with recoverable pin updatesProject workflows and the daily walkthrough
Bring one fix to another branchCherry-pick
Merge with a source-only checkoutPartial-view integration
Find old working copies or release abandoned locks from another computerWorkspace administration
Review a branch integration in the browserProposals

Global options

The global option reference lists connection, trust, compression, ignore, JSON, and diagnostic switches. Their precedence and saved settings are explained under defaults and configuration.

Client diagnostics

Run scribe diagnostics for an offline report of versions, effective endpoints, configuration sources, login status, and client paths. Use scribe diagnostics --json for structured output. It works outside a workspace too, preserves local state, and omits credentials. See the diagnostics reference for fields and inspection failures.

Exit codes

Use the client exit-code reference and JSON output guide when scripting commands. An unknown submit outcome retains its pending state; rerun submit to resolve it.

Connect to an authenticated server

An authenticated server is a tls:// endpoint: a storage server or a gateway, with a separate auth service people sign in at. Connecting takes three things, each saved in your per-user configuration root (never in a workspace): trust in the server's certificate authority, trust in auth's, and a login session. The authentication tutorial walks through them with two users; every option is in the client reference.

Every tls:// endpoint requires TLS 1.3 with the hybrid post-quantum key exchange X25519MLKEM768. A proxy or load balancer that terminates TLS and cannot offer it breaks the connection; pass TLS through to Scribe instead. The client authenticates with mutual TLS: it presents a short-lived access certificate for a device key that never leaves the machine, so nothing a storage server or gateway receives can be replayed. See identity, device keys, and certificates.

Trust a server

Scribe servers use a private certificate authority (CA), so your client has to be told which CA to trust for each server, once. The first connection to an unknown server makes a TLS-only discovery handshake, sends nothing, and shows the CA's fingerprint:

text
scribe: untrusted server tls://127.0.0.1:7447; CA fingerprint BLAKE3:bec9ed90312d4b568ddb7752e5a26e6e80789e9b724b23b20a25a5a86e1c82f9. Run scribe --server tls://127.0.0.1:7447 trust add interactively, or supply --trust-fingerprint BLAKE3:bec9ed90312d4b568ddb7752e5a26e6e80789e9b724b23b20a25a5a86e1c82f9 after verifying it

Compare it with the fingerprint your administrator gives you through a channel you trust (the certs commands print it when they create the CA), then approve it interactively (type yes) or name it:

sh
scribe --server tls://127.0.0.1:7447 trust add
scribe --server tls://127.0.0.1:7447 --trust-fingerprint BLAKE3:bec9ed90312d4b568ddb7752e5a26e6e80789e9b724b23b20a25a5a86e1c82f9 trust add
text
tls://127.0.0.1:7447
CA fingerprint: BLAKE3:bec9ed90312d4b568ddb7752e5a26e6e80789e9b724b23b20a25a5a86e1c82f9
Trust directory: /home/you/.config/scribe/trust

Approve the auth service the same way (its CA is a different one), or give its CA file at login with --auth-ca. This is trust on first use: someone who controls that first connection could present their own CA, which the fingerprint comparison exposes. Approval is scoped to the host name and port, so a gateway and a storage server need one each even under the same CA. The fingerprint is public: CI can pass --trust-fingerprint on its first command, or --tls-ca FILE (SCRIBE_TLS_CA) to use a CA file instead of remembered trust. Commands with --json or without a terminal never prompt.

scribe --server URL trust show lists the CAs saved for a server without connecting. Trust lives in the configuration root's trust/ directory: ~/Library/Application Support/Scribe on macOS, %LOCALAPPDATA%\Scribe on Windows, $XDG_CONFIG_HOME/scribe or ~/.config/scribe on Linux, or --config-dir (SCRIBE_CONFIG_DIR). Records hold public CA certificates only; a corrupt record fails closed. The desktop apps share it.

Log in

sh
scribe --server tls://scribe.example.com:7557 login alice --auth tls://auth.example.com:7443
text
Logged in as alice; session valid for 90 days.

The password prompt is on stderr with input hidden; --password-file FILE is for automation, and a password is never a command-line argument. Login creates a device key on this computer and sends only its public key with the password; auth answers with a credential certificate for the session, 90 days by default. Later commands use it to fetch an hour-long access certificate and renew it on their own. The session belongs to the server it was made for: a gateway and a storage server each need a login. Logging in again replaces the session; scribe logout revokes it at auth and deletes the key; scribe whoami shows the saved login without contacting anything.

For repeated use, set defaults in your shell; explicit arguments override them, and inside a workspace commands use its recorded server:

sh
export SCRIBE_SERVER='tls://scribe.example.com:7557'
export SCRIBE_AUTH='tls://auth.example.com:7443'
export SCRIBE_USERNAME='alice'
scribe login
powershell
$env:SCRIBE_SERVER = 'tls://scribe.example.com:7557'
$env:SCRIBE_AUTH = 'tls://auth.example.com:7443'
$env:SCRIBE_USERNAME = 'alice'
scribe login

A login whose answer never arrived is kept: rerun the same scribe login to resolve it without a second session, or scribe login --discard when the address or username was wrong. Session internals and lifetime settings are in user login and session lifetime.

Sign in as administrator

A session never administers auth, even an auth administrator's. To administer for a while, sign in as administrator; it writes a short-lived administrator credential file instead of saving a session, and needs no storage server:

sh
scribe login alice --admin --auth tls://auth.example.com:7443 --out admin.credential
text
Signed in as alice (auth administrator); administrator credential saved to admin.credential (valid for 8 hours, until 2026-09-28T18:04:11.512Z). Use it with scribe-auth --admin-credential admin.credential or load it on the web administration page.

The username, password and trust arguments are those of scribe login. The file lives for the hours auth is configured with (8 by default), is created private to your account, and an existing file is never replaced. Use it with scribe-auth --admin-credential FILE or load it on the web administration page, which can also sign you in with your username and password directly. For automation or scribe-ctl, create a long-lived one with scribe-auth credentials create. A sign-in whose answer never arrived is finished by rerunning the same command, never issuing a second credential; scribe login --admin --discard --out FILE forgets it. See administrator sign-in.

Machine credentials

CI machines, build agents, and other unattended clients use a credential file instead of a login. An auth administrator creates it for a service account with scribe-auth credentials create --service; the file holds the auth endpoint, the auth CA, the machine's device key, and its credential certificate. Select it per command or per environment:

sh
scribe --credential-file /etc/scribe/ci.credential sync
SCRIBE_CREDENTIAL_FILE=/etc/scribe/ci.credential scribe submit -m "Nightly build"

The file is never stored in workspace state, and --anonymous ignores SCRIBE_CREDENTIAL_FILE. On Unix it must be readable only by its owner (mode 0600) in a directory other accounts cannot write; a file that others can read is refused with ... is accessible to other accounts (mode 644); run chmod 600 on it. On Windows a file whose access list allows another account more than reading its attributes is refused the same way, naming that account's SID. The access certificate fetched for it is cached under credentials/ in the configuration root. See credential files.

When a server's CA changes

Operators replace a CA in one of two ways (certificates and credentials), and the client handles each on its own terms.

A rotation with a handover needs nothing from you. During an overlap the server presents its new certificate with a handover certificate: the new CA, signed by the old one you trust. The client saves the new CA beside the old one without asking, since nothing is believed that your trusted CA did not vouch for; once the server no longer presents the old chain, the client drops the old CA. trust show marks a CA learned this way:

text
tls://scribe.example.com:7557
CA fingerprint: BLAKE3:26e411692a534f4d68414ab4226585414fa41a489382371fd9bc8be07f90bd7b
CA fingerprint: BLAKE3:20d8727bad2779ab4954cf4b373324c14f1facc72ef4ac01999ca8ee871f98b7 (from a CA handover by BLAKE3:26e411692a534f4d68414ab4226585414fa41a489382371fd9bc8be07f90bd7b)

Any other change, or a rotation you did not connect during, is refused before anything is sent, and saved trust is left unchanged:

text
scribe: The CA of tls://127.0.0.1:7447 changed.
  trusted: BLAKE3:bec9ed90312d4b568ddb7752e5a26e6e80789e9b724b23b20a25a5a86e1c82f9
  now:     BLAKE3:2462fcd2d18e01d3ecc2c0d88d152dc1f77ecccc52332b7debb6c36c6501dfec
If your administrator confirms this change, run: scribe --server tls://127.0.0.1:7447 trust replace

Confirm the new fingerprint with your administrator, then replace the saved CAs, interactively (type replace) or by naming the new fingerprint:

sh
scribe --server tls://127.0.0.1:7447 trust replace
scribe --server tls://127.0.0.1:7447 --trust-fingerprint BLAKE3:2462fcd2d18e01d3ecc2c0d88d152dc1f77ecccc52332b7debb6c36c6501dfec trust replace
text
Replaced saved trust for tls://127.0.0.1:7447
CA fingerprint: BLAKE3:2462fcd2d18e01d3ecc2c0d88d152dc1f77ecccc52332b7debb6c36c6501dfec

trust replace presents no credential and leaves a server that still verifies alone. The sign-in service is handled the same way: when you approved it with trust add, the message names its own address; when you logged in with --auth-ca, your session keeps a copy of that CA, which follows handovers too, and a changed one is replaced from a new file the administrator gives you (checked against the sign-in service first), once for each server you have a session for:

sh
scribe --server tls://scribe.example.com:7557 trust replace --auth-ca ./new-auth-ca.pem
text
Replaced the sign-in CA saved for tls://scribe.example.com:7557 (tls://auth.example.com:7443)

A credential file's auth CA is part of the file: a handover is followed while a command runs, and any other change needs a new file from the administrator. With --tls-ca, point it at the new CA file.

The macOS and Windows apps raise the same check wherever they contact a server and show Server Certificate Changed with the trusted fingerprints beside the one now presented; Replace Trust does what trust replace does, pinned to the fingerprint shown (macOS, Windows).

To remove a server you no longer use, forget its saved trust: the whole record, every CA it holds. Name one of the fingerprints trust show lists:

sh
scribe --server tls://old-host.example.com:7447 --trust-fingerprint BLAKE3:… trust forget

The next connection to that server asks for trust again. There is no command that drops a single CA from a record: a handover drops the old CA by itself, and trust replace replaces them all. A record from an older Scribe version cannot be used; the message names trust forget, which then needs no fingerprint.

A desktop scribe://open link from a web page may not name a plaintext endpoint on the network: a tcp:// server or an http:// web address is accepted only for localhost or a loopback address, and otherwise refused with, for example, a link cannot name a plaintext (tcp://) server on the network; use tls://. See opening in the desktop app.

Repositories

sh
scribe repo create <name> [--storage-compression lz4] # letters, digits, '-', '_', '.'; not starting with '.'; ≤ 128 bytes
scribe repo list                   # only repositories you may read on an authenticated server
scribe repo info <name|id>

Creating a repository on an authenticated server requires namespace administration. Choose its permanent storage policy at creation; see compression for the separate storage and transfer settings.

Through a gateway that serves several auth namespaces, name repositories as <namespace>/<name> — scribe repo create studio/game, scribe workspace create studio/game ./game — and repo list reports names in that form. A bare name is accepted only where the gateway has a single or default namespace; the error otherwise lists the namespaces to choose from. Existing workspaces are unaffected: they hold the repository ID.

Workspaces

sh
scribe workspace create <repo> <directory> [--profile NAME] [--view PREFIX]... [--author NAME] [--readonly]
scribe workspace info
scribe workspace profiles
scribe workspace view show
scribe workspace view set --profile programmer
scribe workspace view add Art/Characters
scribe workspace view remove Art/Characters

The directory must not already contain a workspace. --view selects inclusion prefixes (for example --view Assets/Characters --view Source); paths outside the view are neither synced nor addable. Change an existing selection with workspace view set/add/remove; share named selections through a versioned .scribe/config.toml file. View changes are recoverable and protect local work; see workspace views and profiles for the format, local overrides, and cancellation. --author defaults to the logged-in user when a session is saved for that server, then SCRIBE_AUTHOR, then the OS user name; on an authenticated server the signed-in user is the recorded author regardless. --readonly marks unopened tracked files read-only: edit clears the attribute; revert, sync, and submit restore it.

Manage registrations from another computer

sh
scribe workspace list --all-repos
scribe workspace list --repo Game --everyone
scribe workspace remove WORKSPACE_ID --repo Game

These commands need a server connection, but no local working copy. With auth, listing defaults to your registrations; --everyone requires Maintainer. Owners with repository read access can remove their own registrations; Maintainers can remove others. Without auth, any connected client can list and remove registrations. The same rules apply through a gateway.

Removal permanently retires that workspace ID and refuses held locks. It preserves local files and accepted history, but the old identity cannot submit new work. See workspace administration for the Game/Engine example, explicit lock release, retries, and the boundary between remote collaboration administration and server-local maintenance.

Opening files

sh
scribe add [--force] [--offline] <path|dir>...  # new files; directories recurse with ignore rules
scribe edit [--offline] <path>...               # tracked files you will modify
scribe delete [--force] [--offline] <path>...   # tracked files; removes the local file
scribe revert <path>...               # restore the recorded base; reverting an add keeps the file

add --force overrides ignore rules only: it never adds links, names Windows cannot represent, anything under .scribe/_state/ (reserved local workspace state; the rest of .scribe/ is ordinary repository content), or files outside the workspace. delete refuses a locally modified file unless --force. The removed file goes to .scribe/_state/recovery/ whether or not it was modified, and revert restores the file from its base. revert of an edit moves the local content there too. Each path answers for itself: a refused path is reported as skipped while the others are still opened.

While a merge or other conflict resolution is active, add, edit, and delete are refused: work opened then would be submitted inside the integration commit and inherit its lineage. Finish it with scribe merge --continue, or discard it with scribe merge --abort, and open the unrelated paths afterwards. scribe reconcile --scan reports such files instead of opening them.

Move files and folders

sh
scribe move Assets/Characters Assets/Actors
scribe status
scribe submit -m "Move characters into Actors"

For one file, use scribe move Assets/Old.uasset Assets/New.uasset. The destination is an exact new path. A folder carries its untracked and ignored contents too; only tracked files are opened for submission. Both subtrees must be fully in view. Source files may belong to earlier pending moves or be opened for edit; other opened work must be submitted or reverted first. Local edits, opened or not, travel with the file and submit with the move as one entry. Case-only leaf and folder renames use the same explicit command. Existing destinations, links, invalid names, and colliding source names are refused.

A rename done in the file manager or an editor shows up as a missing tracked file plus an untracked one, and would submit as a delete and an add. Like p4 reconcile, scribe move --detect pairs them afterwards:

sh
scribe move --detect --dry-run     # list the pairs it can vouch for
scribe move --detect               # record each pair as a pending move

A pair is a missing tracked file, opened for edit or not, and an untracked file holding exactly its recorded bytes: only untracked files of the same length are hashed, and only a content id equal to the baseline's pairs. Identity is content, so a file edited on the way is not paired, and two missing files with the same bytes (or two untracked copies of one) are reported as unpaired rather than guessed; record those with move --after. Recorded pairs are ordinary pending moves, already applied on disk: they show in status, submit together, and move --abort --id <ID> undoes one by restoring the original path. The app offers the same under Submit ▸ Find Renames….

Engine editors and IDE refactors often change a file as they rename it: an Unreal asset rename rewrites the package path stored inside the .uasset. No content match can vouch for such a pair, so name it:

sh
scribe move --after Content/Hero.uasset Content/Actors/Hero.uasset

--after records a rename that already happened. The source must be gone and the destination must be a present, untracked regular file; its bytes may differ. The source may be opened for edit. Submit records the move and the new bytes as one history step, and move --abort puts the current bytes back at the source path. --after takes one tracked file; a folder renamed outside Scribe is recorded file by file.

Multiple moves can remain pending and be submitted together. Rename chains such as A -> B -> C retain the original accepted history; submission records A -> C. A folder can include files renamed by earlier pending moves.

status lists each move's ID. move --abort --id <ID> undoes a selected move; without --id, abort selects an interrupted move first, otherwise the latest. Independent moves can be undone in any order. If a later move depends on the selected move, abort that later move first. Each undo restores the previous location and pending state while retaining the latest local bytes.

If interrupted, move --continue resumes that move. Only one move can be interrupted at a time; finish it before starting or undoing another. A vacated source location remains reserved until submission or abort, so cycles and source-path reuse require finishing the earlier move first. Moves refuse to overwrite an obstruction. Original work in transit remains under .scribe/_state/staging/move-<operation>; if that entry, the source and the destination are all gone, --continue refuses and --abort retires the move without restoring anything, and says so. Use this abort command rather than reverting individual move endpoints. Updating source revisions requires finishing pending moves first. If a move is rejected because its source is stale, abort pending moves, update the original files, then reapply the moves.

Submit validates both endpoint locks and the source's exact revision. A source lock held by this workspace is also acquired for the destination with a new generation; the original remains locked for branches that still use that name. Release both when finished. A folder move is limited to 50,000 tracked files and 100,000 discovered filesystem entries. A workspace can have up to 1,024 pending moves with at most 32 MiB of combined journal data.

Status and scanning

sh
scribe status                # opened paths and pending workflows; no network
scribe status --scan         # also unopened tracked changes and untracked files
scribe status --verify       # scan and hash tracked contents
scribe status --remote       # add current branch head, main lock rules, and holders
scribe status --scan --remote # combine discovery and live collaboration state
scribe scan [--verify]       # same as status --scan
scribe ignore explain <path> # the deciding rule, or the excluded parent
scribe diff [<path>...]      # unified diff of opened text files against their baseline
scribe diff --revision 41 <path> # against an accepted change instead

diff is read-only and needs the server for the baseline bytes; it prints the usual ---/+++ unified format for opened text files, Binary files ... differ for files with NUL bytes, and too large to diff above the 2 MiB review bound. Nothing is opened, locked, or written.

Status shows one row per canonical path. Each row combines opened intent and actual local state with recorded move sources/destinations, offline reconciliation, deferred sync, resolution choices/groups, and optional live lock information. Moves also have an operation summary with IDs and dependencies for resuming or aborting a specific move. Chained moves show the original accepted source and final opened destination; intermediate names do not become phantom changes.

Unresolved conflicts are distinct from saved choices awaiting application. The workflow summary reports planning, conflicts, applying, resolved, or aborting; resolved means the local plan is applied, not submitted. Pending submit outcomes, branch operations, branch switches, and sync journals are also visible, with relevant next-command guidance: Next: scribe submit ... when files are opened and nothing else is pending, Next: scribe reconcile ... only when an edit was opened offline, and the pending workflow's --continue / --abort otherwise. A trailing Summary: line counts paths by intent so a long listing still ends with the totals. A file can be opened, modified, deferred, and pending reconciliation at the same time; none of these facts hides the others.

Default status inspects opened and workflow paths only. It explicitly says that unopened changes and untracked files were not scanned; an empty report never claims the whole workspace is clean. --scan widens discovery. --verify hashes contents to detect changes that size and modification time alone can miss.

A scan trusts an unchanged size and modification time only when the recorded state is settled: the file's modification time must be older than the moment the workspace recorded it by that filesystem's timestamp granularity. A modification time with sub-millisecond digits cannot be reproduced by a later save, so it is trusted immediately; a whole-second time (FAT, some network filesystems) is trusted two seconds after it was recorded, and a sub-second time one second after. Otherwise a save of the same length within that window would be invisible, so those files are hashed during an ordinary scan until their contents change. Ignore rules never hide tracked changes. Inspection failures are attached to the affected path without suppressing other rows or treating failures as missing files. A directory in place of a file, or a file blocking a parent directory, is reported as not_file, including file/directory rename conflicts. Status never changes files, opened intent, reconciliation state, or locks.

Remote information is not checked by default. Even a locally recorded lock acquisition is not evidence of current ownership. --remote reads the current branch head, accepted main .scribe/config.toml, and repository lock holders within the workspace view. Each reported path then shows required/optional locking and unlocked/this-workspace/other-workspace ownership. Lock-only paths have no local content inspection; fetching lock information does not hash those assets. These separate server reads are observations, not a transaction or a promise that a later submit will succeed. The branch head and sync watermark are shown as revision numbers, not a misleading count of changes behind.

If an explicit remote check fails, status still prints the full local report, marks remote information unavailable, leaves lock ownership unknown, and exits 1. It publishes no partial lock page. Local inspection or ignore-rule problems also produce exit 1 with the report. Ordinary file changes, conflicts, and pending workflows do not themselves produce a nonzero exit. JSON output uses the same per-path model; see the output reference.

Submitting

sh
scribe submit -m "message"
# Or publish only selected opened files:
scribe submit -m "Fix movement" -- Source/Player.cpp Source/Movement.cpp

Use submit for the action and change for the accepted result, identified by a repository-wide change number. Technical specifications also use commit for the immutable history object and commit envelope for its durable record.

A submit publishes directly to the server—the equivalent of Git’s "commit and push". Without paths it includes all opened changes; with paths it includes only the selected exact opened files. Unselected work and locks remain intact. Paths are relative to the current directory, must stay in the current workspace, and can name deleted files. Directories and unopened files are refused. Selecting a move endpoint includes the complete folder move and dependent rename chain. Active merge, cherry-pick, and conflict resolution workflows require a full submit.

The client chunks and hashes each selected opened add and edit, uploads only chunks the server lacks together with manifests and the change set, then persists the candidate and operation ID before sending submit. It finalizes local baselines after the server's durable acknowledgement. A file that changes while it is read or uploaded is refused.

  • accepted: prints the change number, file count, chunks sent, bytes on the wire versus logical bytes, and server flush count;
  • rejected (base_mismatch, locked_by_other, path_collision, …): no new history is accepted, and opened work is retained. For ordinary stale opened files run scribe resolve --update, choose or edit each resolution, and submit again. Pending moves must be finished or aborted before updating their source revisions. If the target advanced, a grouped merge or partial-view integration must be aborted and restarted;
  • unknown submit outcome (reply lost): exit 3. The candidate is kept; the next scribe submit first asks the server what happened. If no outcome is recorded, it replays the same ID and exact intent, including merges. It never infers a rejection from absence. New command-line paths and message do not replace the pending candidate or start a second submission in that invocation. Immutable uploads finish before the candidate is persisted; interrupted uploads can safely be re-analyzed and repeated.

While a submit outcome is pending, resolve it with scribe submit before using sync, add, edit, delete, or revert. This keeps the candidate's local state intact until the server confirms its result. Status and scan remain available.

Importing an existing tree

A first population of a repository from an existing project is usually larger than one submit may be. Acceptance is a single durable transaction taken under the repository's publication gate, so the server caps the change-set entries it takes at once ([limits].max_submit_entries, 100,000 by default) and refuses a larger submit with limit_exceeded. Use import for that first population:

sh
scribe add .
scribe import -m "initial import" --dry-run   # states the plan, sends nothing
scribe import -m "initial import"

import submits the opened adds in path order as a sequence of changes, each within the cap it reads from the server rather than one compiled into the client. If the server refuses a batch anyway — a lower cap, or a gateway reporting the default — the batch is halved and re-sent. Every change carries the message verbatim; the plan and each accepted change are reported on stderr as the import runs.

Import is resumable, and a million-file import will be interrupted. Each batch is an ordinary submit: its operation id is persisted before transmission, and an unknown outcome is resolved by asking the server rather than guessed. Just run the same command again — it resolves the batch that was in flight, then continues with the paths the server has not accepted. Re-running a finished import succeeds and says so, so it is safe to repeat until it does.

import accepts only adds, and refuses an opened set containing an edit, a delete, a keep, or a move destination, naming the path. Splitting is sound here because every entry is an add against an empty repository, so each intermediate change is a coherent prefix of the tree. It is not sound in general: the delete half of a rename could land in one change and the add half in the next, publishing a state in which the file exists nowhere, and rename pairs can be arbitrarily large, so no batch size fixes it. Ordinary work — including a large reorganization full of renames, which is exactly what the cap targets — stays one atomic scribe submit. For the same reason there is no submit --split.

An administrator can also raise max_submit_entries for a one-off import, but that blocks every other submit on the repository for the length of the acceptance; import does not.

Syncing and exporting

sh
scribe sync                         # to head
scribe sync --revision <change>     # to a pinned change, including an older one
scribe export --revision <change> --to <empty dir> [--repo NAME] [--view PREFIX]...

Sync writes a journal before touching files, applies it in batches, reuses unchanged chunks from the local copy and the cache, verifies every reconstructed file's content id, and advances the watermark only when everything is applied. An older target uses verified backward changes when the workspace has a verified baseline; otherwise sync proves a full snapshot and reconciles against it. Every local file about to be replaced is moved to .scribe/_state/recovery/ first, including files that equal their recorded base, and the report names each location, or a count when more than ten were displaced. See the recovery area for listing and pruning.

Sync never replaces local work: a path whose server version differs from its baseline is skipped and listed as deferred when it is opened, when its bytes are not its baseline's (edited without scribe edit), or when an untracked local file sits where the server now has one, and a server-side deletion of such a file is deferred the same way. A deferred path stays deferred on every later sync until you decide: scribe revert <path> takes the server's version and moves your bytes to recovery, and an opened path can instead be reconciled and submitted. Only a view change replaces rather than defers, since the selection you chose is the decision.

A path whose baseline already is the server version has nothing to apply and is completed as unchanged, opened or not: local edits to it, including ones made after the submit that recorded that baseline, are left exactly as they are. An accepted submit advances the synced marker itself when its change number is the next one after the synced change (one sequence serves every branch, so nothing else can have landed between them) and no journal, view change, mount, or repository-configuration edit is involved; otherwise the next sync lists the submitted files and completes them as unchanged.

Files another program holds open (Windows sharing violations) are reported and retried on the next run, which resumes from the journal. An interrupted revert retains its deferred update; rerun revert to finish it.

export writes a pinned tree into an empty directory with no workspace state.

The recovery area

sh
scribe recovery list                       # every retained entry: time, reason, original path, location
scribe recovery prune --older-than 7d      # remove retained entries older than a week
scribe recovery prune --all                # remove every retained entry

Sync, revert, delete and conflict resolution move each local file they would replace or remove into .scribe/_state/recovery/, under an opaque name with an index beside it that records the original path, the reason (sync-update, sync-remove, delete, revert, resolve, project), the entry's operation id and the time. Even files that equal their recorded base are retained: an editor can save after a content check or through an open handle after the file moves. Budget recovery space for the old versions displaced by updates and view changes. Nothing is pruned automatically. Close editors using retained files and review the recovery entries before pruning. prune needs --older-than (a number with s, m, h, d or w) or --all, removes only entries Scribe retained and can map back, and keeps intent archives such as submit-<operation>.pending and any entry whose index it cannot read, naming each one it kept.

Downloading one historical file

sh
scribe get Assets/Hero.uasset --revision 123 --to ./Hero-old.uasset
scribe get Assets/Hero.uasset --revision 150 --branch art --to ./Hero-art.uasset
# Outside a workspace, select the repository explicitly:
scribe --server tcp://localhost:7447 get Assets/Hero.uasset --repo game --revision 123 --to ./Hero-old.uasset

get downloads exactly one file from the selected branch's snapshot at the given repository change number. The source path is always repository-relative, even when invoked from a workspace subdirectory; use the spelling that existed at that revision. For a renamed or deleted file, consult log for its historical path and an earlier revision. A missing path or directory is an error.

Without --repo, the repository and default branch come from the workspace. With --repo, the default branch is main. --branch accepts a name or ID and selects another branch without switching the workspace. Revisions outside the selected branch's lifetime are refused. The reported file_change is the last change to that file at the selected snapshot and can precede revision. Workspace views do not restrict this explicit download; normal repository read authorization still applies.

--to is a new local filename, relative to the current directory or absolute. Its parent directory must already exist. Existing files, directories, and symlinks are refused, including destinations created by another process during the transfer. Destinations inside .scribe/_state are refused. There is no overwrite or force option.

The client uses an exact indexed lookup and fetches only this file's manifest and chunks. It verifies chunk identities and the complete reconstructed file, preserves the stored executable mode, flushes it, and publishes it with an exclusive rename. It does not change the workspace branch, baselines, opened records, sync watermark, locks, or object cache. A download placed inside the working tree is an ordinary local file; use add if you intend to track it.

Reconstruction uses a unique .scribe-get-<id> directory beside the destination. Normal failures clean up staging and leave the final filename absent. A killed process may leave its staging directory; after confirming that process is no longer running, it can be removed and the command retried. If interruption happens after publication, the destination is already the verified file and a retry refuses to replace it. A directory-flush failure reports that the verified file has been published. Downloads require server connectivity.

Branches

Use the branch workflow to create a development branch, switch the workspace, and publish changes. Branches retain stable identities; locks remain repository-wide. The requirements and limits explain clean-workspace prerequisites and revision selection.

Merging and resolving

A merge is prepared locally, resolved, then submitted as shared history. Follow the branch and conflict guide for a complete workflow, including stale submissions, side selection, continuation, abort, and external tools. Planning checks locks before writing any file: a path another workspace holds refuses the plan, and a path the active policy requires a lock for is acquired for this workspace first. merge --abort releases exactly those acquisitions. The merge and resolve references list all options.

History, info, locks

sh
scribe log [path] [--limit N] [--before CHANGE] [--after-path HISTORICAL_PATH]
scribe log <path> --first-parent # omit merge-source revisions; retain branch origins
scribe log <path> --all-branches # discover related submitted work on every branch
scribe describe <change> -m "…" # correct a change's description after acceptance
scribe describe <change> --history # every description ever recorded for it
scribe info <path>              # base, local state, head revision, lock holder
scribe lock acquire <path>...   # exclusive across branches, held until explicitly released
scribe lock release [--force] <path>...
scribe lock list [--prefix DIR]

File logs follow recorded moves, branch origins, and merge-source ancestry by default, newest change first, with shared ancestors shown once. Sources stop at the revision actually merged; later unmerged source edits do not appear. --before CHANGE alone is an exclusive pagination bound on returned revisions, including source revisions reached through newer merges.

Each log entry identifies the branch where it was accepted. JSON includes branch (stable ID), branch_name (current name), and merge_parent (source branch ID and revision, or null). A branch rename changes the displayed name, not the revision's identity. Repository logs without a path still follow only first parents. info reports the target branch's latest path state, excluding merge-source revisions that did not change the target.

Merge ancestry records which history participated, not which source bytes were selected during conflict resolution. An ancestry-only merge can expose source revisions without adding a target revision to that path's log. File moves carry explicit predecessor records; logs retain earlier names and revisions. Use --all-branches to search related work beyond this ancestry.

A successful submit releases your locks on submitted files atomically with the accepted change. Use scribe submit -m "Description" --keep-locks to retain them. Locks on files outside the submission remain held. Rejections keep locks; after an unknown outcome, recover the saved submit before deciding what happened. A retry preserves the original keep-locks choice.

A submit touching a path locked by another workspace is rejected. lock release releases only this workspace's acquisition; --force releases someone else's, and on an authenticated server needs the maintainer role.

Locking a path that does not exist at head is normal for new files, deletions, and move destinations, but the number of such locks a repository holds at once is bounded (4,096 by default). limit_exceeded on lock, edit, or reconcile means those pending locks must be submitted or released first; scribe lock list lists them.

File-history rows expose path (the actual historical name) and optional moved_from: {path, change}. To continue a file log, pass both the last row's change to --before and its path to --after-path; this retains entries tied at the same changeset. --first-parent still follows explicit local moves.

Merges follow recorded file relationships across renames and folder moves. An edit on one branch is matched to the renamed file on the other branch; the name and contents are merged separately. See rename-aware merging.

Correcting a description

A submit message is part of the accepted change and is never rewritten. When it needs fixing — a typo, the wrong ticket number, a missing reviewer — scribe describe <change> -m "…" records a corrected description beside it. log then shows the description in place of the message, with who revised it and when; --json carries both (message and note). Every description ever recorded stays on the server: scribe describe <change> --history lists them oldest first, and the submitted message remains in the commit and in log for clients that predate descriptions.

You can describe your own changes; other people's need a maintainer. A change is yours when your account submitted it: the server decides from the owner it recorded for the submit operation, not from the author text, so a change accepted before the server used authentication has no owner and needs a maintainer. The server attributes each revision to the signed-in principal, whatever author the workspace names. Two people describing the same change at once cannot overwrite each other: the second is refused with note_revision_stale and sees the current description before deciding again (--expect pins the revision explicitly for scripts).

Rename-aware merging

sh
scribe merge art
scribe resolve
# Choosing any displayed member chooses the complete rename group:
scribe resolve --theirs Characters/Hero.uasset
scribe submit -m "Merge art changes"

The planner uses both branches' changed-path indexes and the recorded revision lineage to match files to the pinned merge base. It follows chained renames and handles each tracked file in a folder move. It does not infer identity from matching filenames or contents, or from filesystem moves made outside Scribe. An earlier merge that kept target bytes still supplies usable ancestry.

Changes since the baseResult
One side renames; the other editsKeep the renamed destination and merge the contents there.
Both sides choose the same new nameKeep that name and merge contents normally.
Both choose different namesA rename/rename group requires an explicit choice.
One renames; the other deletesA rename/delete group requires an explicit choice.
A destination is occupied, collides by case/normalization, or conflicts with a file/directoryA destination-collision group requires an explicit layout choice.

Disjoint text edits inside the view can merge automatically under the existing size and work limits. Conflicting text and binary edits require resolution. Choosing --ours for an included path whose source and target differ retains the target state and records a keep decision, shown in scribe status as opened for keep. Excluded automatic decisions remain in the resolution journal without opening a local file. The submission carries these decisions so the server can verify coverage of the whole source delta. Excluded conflicts require explicit inclusion; see partial-view integration. When the name is agreed, --ours and --theirs choose contents at that destination, retaining the rename. When names conflict, these choices select the corresponding layout for the entire group, including colliding tracked files. Review the displayed members before choosing a side. Independently reusing a vacated filename never silently redirects the original file's edits into the new file.

resolve lists the group members, original/target/source names, and any path conflict. Selecting one member with --ours, --theirs, or --working selects the whole group. The choice is saved atomically before application; interrupted application resumes with scribe merge --continue. --all selects every group.

For a content conflict with an agreed renamed destination, write the resolved file at that destination before using --working. Scribe removes the obsolete source as part of the recorded rename; a missing destination is refused. For a name or destination conflict, arrange the desired files among the displayed group paths first, then use --working to capture that layout. Immutable inputs remain available through scribe resolve --export ./inputs.

Every replaced file is retained in .scribe/_state/recovery. Abort restores the pre-merge target layout and retains work written after a choice. Accepted merges record target-side move predecessors when a file is added at a new path and its old target path is deleted. Existing lock and submit checks still apply.

If the target advances and a grouped merge is rejected, abort and restart the merge so names and destinations are planned against the new target. Incremental resolve --update is refused for grouped merges; keep/reapply recovered manual edits as needed. Ambiguous lineage with multiple base-file ancestors is refused before applying the plan; abort and choose an appropriate earlier explicit --base to plan again.

Planning remains bounded to 100,000 touched paths, 32 MiB of path names, and 128 MiB of resolution records. Each atomic rename/collision group has at most 1,024 paths. Origin-query batches have a shared metadata work budget and return an error rather than guessing when it is exceeded. These limits are checked before applying the plan. No complete repository tree or unrelated working-file contents are scanned for rename detection.

Cross-branch file history

sh
scribe log Assets/Hero.uasset --all-branches --limit 20
# Continue using the snapshot, change, and historical path from the last row:
scribe log Assets/Hero.uasset --all-branches --at 150 --before 123 --after-path Assets/Hero.uasset --limit 20

The selected file is the latest accepted revision of that path on the workspace's branch at the query snapshot. A deletion can select the last file incarnation; a later ordinary add at the same path starts a separate incarnation. To inspect the earlier incarnation, select a snapshot before the replacement with --at. A path absent from that branch's stored history returns no rows.

The server follows recorded predecessor relationships in both directions: edits, deletions, explicit moves, and per-file merge entries. Branch snapshots share their original revisions, so unmerged sibling and nested branch edits are found without switching workspaces. Both endpoints of a move can appear at the same change. Independently added files remain separate even when their names or bytes match. An ancestry-only merge does not itself create a per-file identity link between independently added files.

Each row shows the accepting branch, historical path, and one of:

  • in ancestry: the accepting commit is reachable from the selected branch at the snapshot, including recorded merge parents;
  • related; outside ancestry: a related revision on another line of development whose accepting commit is not reachable there.

These labels describe commit ancestry, not whether a merge selected that revision's contents. In particular, a merge that kept the target bytes still records the source ancestry. Unsubmitted and offline local edits are not server history and do not appear.

The first request pins the current repository change number and prints it as snapshot; JSON calls this history_snapshot. Reuse it as --at on every page, along with the last row's --before and --after-path, to keep membership and file selection stable while teammates submit or rename files. Branch display names remain current. --all-branches requires a path and conflicts with --first-parent; --at requires --all-branches.

Queries use an indexed revision graph and sparse merge metadata; they do not scan repository trees or download file contents. Work is bounded to 100,000 metadata visits and a 32 MiB lineage path budget. Exceeding either limit returns an explicit error, never partial history. Pagination bounds the returned rows; each request still discovers the connected lineage at the pinned snapshot. The operation requires repository read access and does not modify the workspace.

Mandatory lock rules

Put lock rules in the repository's .scribe/config.toml, alongside any workspace profiles:

toml
version = 1

[locks]
rules = [
  '*.uasset',
  '*.umap',
  '*.blend',
  '*.psd',
  'Art/**',
  '!Art/Generated/**',
]

Rules apply to any file type. Matching ignores case and Unicode normalization, under the same fold as path collisions (*.psd also locks Hero.PSD), is root-anchored, and uses Git-style globs and escaping. TOML comments belong outside rule strings. A pattern with no slash matches a filename at any depth; a leading slash anchors at the root. Last matching rule wins. ! removes the mandatory requirement; unmatched paths permit ordinary editing. A trailing / selects the subtree. Unlike ignore traversal, a directory rule cannot hide a later exception. Only the root config participates; there are no nested or local lock overrides.

The accepted locks section on main governs every branch. Local edits, old sync revisions, and branch-local proposals cannot override it. Changing the ordered lock-rule values on main requires Maintainer permission, including through merges, section removal, file deletion, or moves. Profile-only changes and TOML formatting changes retaining the same rules remain Writer operations. The old policy governs its own updating changeset; after acceptance, later submissions use the new policy. Removing rules never releases existing locks. Without a locks section, mandatory locking is disabled; manual locks remain enforced.

The entire configuration is validated on every branch: UTF-8 TOML, at most 64 KiB, version 1, known fields only, and at most 1,024 lock patterns. Rule diagnostics use actual TOML source lines. See repository configuration for the complete schema, limits, and permissions.

sh
scribe lock rules                  # active server policy, regardless of workspace branch
scribe lock rules Art/Hero.psd     # requirement and deciding rule
scribe edit Art/Hero.psd           # acquire a required lock, then open for editing
scribe status --remote             # current holders and requirements alongside local status
scribe info Art/Hero.psd           # includes requirement, rule, and active policy revision
scribe lock release Art/Hero.psd

By default, every new edit contacts the server for the current policy, including edits to nonmatching files. If the server cannot be reached, it fails before changing opened state or read-only permissions. Already-opened work remains editable offline; submit needs the server. Ordinary status and scan remain local; status --remote explicitly adds network queries. Repeating edit on a file that is already opened reports it as skipped PATH: already opened because the opened record is unchanged, but it still checks the policy and reacquires any required lock first, so it is the way to recover a lost or revoked lock. An online edit also warns on stderr when another workspace holds a lock on a file it just opened, since submit will refuse that file until the lock is released. Use explicit edit --offline to open local work without connecting; see below.

For a batch edit, required locks are acquired before any opened-state or permission changes. Successfully acquired locks remain held if a later acquisition fails; inspect with lock list, retry, or explicitly unlock. A lost lock reply leaves files unopened; retrying acquisition returns the same lock while this workspace still holds it. Locks survive disconnects. Successful submissions release submitted locks by default. There is no automatic expiry or silent forced release.

Submit rechecks the current policy and actual ownership in the durable metadata transaction. A newly introduced rule or revoked lock can therefore block an already-opened edit. Acquire the lock and submit again; local work is retained. Mandatory ownership also covers adds, deletes, and move endpoints. Use lock explicitly before submitting matching new files or deletions. A move can acquire its destination atomically from its owned source lock; a destination held by another workspace always rejects the move. With --keep-locks, the source lock is retained and the destination is acquired; otherwise both submitted endpoints are released. A merge or cherry-pick acquires the locks its own result needs while planning, before it rewrites any working file, and refuses to plan over a path another workspace holds; keeping the target's version needs no lock. merge --abort releases what that merge acquired, and an accepted merge releases submitted locks unless submit --keep-locks was selected. See branches and conflict resolution. Rules describe cooperative editing requirements; they cannot prevent arbitrary filesystem writes, and server enforcement never trusts a client's local policy.

Offline work and reconciliation

For a step-by-step exercise, follow Work offline.

In an existing workspace, explicitly open a tracked file without connectivity:

sh
scribe edit --offline Art/Hero.psd
# Edit in your usual tools.
scribe status
# When connected again:
scribe reconcile
scribe submit -m "Update hero"

edit --offline makes no server connection. It durably records local intent before making a read-only file writable, and marks it pending reconciliation. It does not acquire or promise a lock, and does not consult a local or cached .scribe/config.toml as authority. Ordinary online edit never silently falls back to offline mode. Repeating it on an offline-opened file can acquire a lock, but leaves the marker until reconciliation checks revisions too.

For planned offline work, run ordinary edit on required assets before disconnecting: acquired locks survive disconnects. Use submit --keep-locks if you need to retain submitted locks too. Already-opened files remain writable offline. Administrators can explicitly revoke locks, so submit always rechecks ownership. add, delete, and move record intent locally first; their opened records start pending reconciliation. add and delete then make the reconciliation round trip at once for the paths they just opened, so a file the lock policy covers is locked as edit would lock it, and report what it found. An unreachable server skips that check and leaves the records pending; --offline skips it without trying, so offline work never waits on a server that is down or behind a lost VPN. The desktop apps do the same, recording without the server when they know they are offline. Local conflict resolutions that retain opened work set the same marker. Reconciliation is a preparation step, not a mandatory extra round trip before every submit: submit can accept valid work directly and never trusts this local marker.

reconcile checks all opened paths against one pinned head of the workspace's branch, reads the current accepted main lock policy, and acquires the required locks, including new paths, deletions, and both move endpoints. It also reports other-workspace manual locks on optional paths. Exact state reads are batched; there is no full server-tree download. No file bytes, baselines, sync watermarks, or recovery contents change. Ready paths clear their marker; blocked paths keep it. Readiness describes the checks at the reported revisions, and is not a promise that a later submit will succeed: policy, revisions, or ownership may change in between.

A failed lock acquisition does not stop checks for unrelated files. Locks successfully acquired earlier remain held, including locks on paths that need revision resolution. A transport or storage failure stops reconciliation; markers remain pending if final local recording has not completed. Retry the same command: reacquiring a lock still owned by this workspace is idempotent, including after a lost reply. Use scribe lock list and explicit scribe lock release to release acquisitions you no longer need. A pending submit outcome must be resolved with scribe submit before offline edit or reconciliation can proceed.

For a stale revision, use the existing resolution workflow, review the result, and reconcile again:

sh
scribe resolve --update
scribe resolve                         # inspect remaining conflicts
# Edit an unresolved file, then record that choice:
scribe resolve --working Art/Hero.psd
scribe reconcile
scribe submit -m "Resolve offline work"

Binary conflicts may need an external editor or an explicit ours/theirs choice. If a pending move's source advanced, abort the move first (dependent moves in reverse order), resolve the source, and repeat the move; automatic updating of pending move sources is not implemented. Reconciliation itself never chooses versions, steals locks, or discards local content.

If stale work belongs to a grouped merge, abort and restart that merge instead of using resolve --update; retain and reapply recovered manual edits. See rename-aware merging.

To collect edits made by tools outside Scribe:

sh
scribe scan --verify                 # read-only discovery; works offline
scribe reconcile --scan              # online: discover and open tracked edits, then check

--scan hashes tracked contents using the existing verifying scan, so it can be expensive on a large workspace. It opens discovered modified tracked files and reports missing tracked files and untracked files. It does not infer deletes, adds, or renames: use delete, add, and explicit move for those intentions. Ignore rules still affect only untracked discovery. An opened edit/add with a missing local file or an opened deletion with a reappeared local file is reported as blocked, preserving the recorded intention and any local content.

Reconciliation exits 0 when all opened paths pass its checks, or 1 with a per-path report when some need attention. Discovery-only missing/untracked paths are listed separately and do not change that exit status. With --json, both outcomes emit one report object; connection and other execution failures use the normal client error output.

Ignore files

.scribeignore files are versioned and nest; .scribe/_state/ignore is local; a --global-ignore file has the lowest precedence. Git-style syntax; deeper files win; the last match in a file wins; an excluded directory is not entered. A malformed rule stops discovery rather than being skipped. Details in ignore-and-paths.md.

Typical day

sh
scribe workspace create game ~/game --view Assets --view Source --author ann
cd ~/game && scribe sync
scribe lock acquire Assets/Hero.uasset && scribe edit Assets/Hero.uasset
# ... work ...
scribe status
scribe diff
scribe submit -m "hero rig update"
scribe lock release Assets/Hero.uasset
scribe sync

When the active policy already requires a lock for the asset, scribe edit acquires it automatically and the explicit lock step is unnecessary.

Compression

Repository storage and connection compression are independent. Both default to none. Choose the permanent repository storage policy at creation:

sh
scribe repo create game --storage-compression lz4
scribe --transfer-compression lz4 workspace create game ./game

The workspace creation command saves that transfer preference. Change it later from inside the workspace:

sh
scribe workspace config --compression none
scribe workspace config --compression lz4

scribe workspace info reports the saved preference. A global --transfer-compression none|lz4 overrides it for one command, for example scribe --transfer-compression none sync; it does not change the saved value. scribe repo info game reports the repository's immutable storage policy. There is no API or CLI to change that policy after creation.

LZ4 only compresses object data, leaving control messages unchanged. Objects that do not shrink remain raw on disk; transfers similarly retain raw blocks when compression does not help. Compression does not change chunk IDs, manifest IDs, deduplication, or accepted history. Working-copy files remain ordinary uncompressed files. The workspace object cache uses raw storage.

Submit and sync JSON include protocol_bytes_sent and protocol_bytes_received for that connection (handshake, metadata frames, and encoded object data; TLS and TCP overhead excluded). Existing bytes_sent/bytes_fetched workload counters count original object bytes and should not be used to measure wire compression. node tools/scenario.js compares all four storage/transfer settings on identical repetitive and random inputs, reporting submit/sync wall times, logical throughput, protocol byte counts, and physical object-file sizes.

Recover an interrupted submit after server restore

Run scribe submit to resolve the pending operation. If its result is still recorded, the client applies that result. If the repository has a new incarnation and no result is recorded, the client saves the exact old candidate under .scribe/_state/recovery/submit-<operation>.pending, clears the blocking pending row, and reports the archive path. Your current files and opened actions remain.

Then run scribe sync. Opened files are deferred; old baselines cannot be submitted merely because change numbers happen to match restored history. For edits, run scribe resolve --update, make an explicit choice (for example, scribe resolve --ours PATH), and submit. Revert is also available and retains displaced bytes in recovery. Abort pending moves before updating their source revisions. An opened add that remains absent on the restored server can be submitted normally after sync.

Without an incarnation change, a missing outcome remains unknown and the client retries the same intent. It never clears a pending operation merely because its outcome or an object is unavailable.

Repository mounts

Use .scribe/config.toml to pin child repositories into virtual directories. scribe workspace mounts lists declarations, and scribe sync materializes selected children under their own permissions. See repository mounts for the config format, pin updates, partial failures, and local-work protection.

Project orchestration

Ordinary edit and submit still handle files in one repository, including parent files beside mounts. The new project group coordinates the parent and selected mounted workspaces. Follow the practical game-and-engine walkthrough to choose between these workflows.

Use scribe project status --scan --remote to inspect selected components, scribe project sync to materialize the pinned composition, and scribe project update NAME [--revision N] to stage an explicit parent pin. scribe project publish -m MESSAGE prepares opened changes, publishes children first, and commits the parent with their confirmed revisions. Recover from the root with scribe project publish --continue or --abort. See project workflows for selection, recovery, and local-work protections. No cross-repository atomicity is implied.

Cherry-pick

Use scribe cherry-pick <CHANGE> --from <BRANCH> to stage one ordinary source change, then review and submit. --continue resumes a saved pick; --abort preserves displaced edits in recovery. See cherry-pick workflows for backports, conflicts, history and web/SDK examples.

Integrate without downloading excluded assets

merge and cherry-pick work with partial workspace views. Both still integrate the complete selected history; clean excluded paths reuse accepted content by identity. Conflicts outside the view require explicit inclusion before acceptance. See the worked partial-view example.

Transfer concurrency

Larger uploads and downloads automatically use up to four session-bound data connections. Small transfers stay serial. Use --transfer-jobs N or SCRIBE_TRANSFER_JOBS to set a limit from 1 to 8; 1 forces serial operation. See parallel transfers for configuration and bounds.

Source docs/client-usage.mdSnapshot 2b8dcccb