🔒 TouchQuill is in closed testing.Want to try it? Get in touch →

How to think about TouchQuill

Before you type your first command, three ideas are worth internalizing. The rest of these docs keep coming back to them.

1. Content has an address, not a name

Every file, directory tree and commit is stored under an address computed from its content (a BLAKE3 hash). The consequences are practical: an identical texture used in two places is stored once; the server only sends objects you don't already have; and if anything gets corrupted in transit, the client notices because the hash won't match. You don't have to take integrity on faith: you can compute it.

2. You don't merge binaries: you take them

Textures, meshes and blueprints have no meaningful merge. Rather than pretend otherwise, TouchQuill builds locking into the core: whoever holds the lock commits; the server rejects a push touching a locked file from anyone else. The queue means "waiting for a file" no longer involves pinging people on Slack.

3. The change matters more than the commit

A commit is a technical record; the logical change (Change ID, ch-…) lives longer: it survives fix-ups (amend), message edits and rebases. Reviews, locks and history refer to something stable. On top of that sits the operation log: almost anything can be undone with tq undo, including recovering unsaved work after an accidental restore.

Install and first commit

The client is a single tq binary (Linux, Windows, macOS). You only need the server, tq-server, once you want to share work.

# Linux: rpm package · Windows: installer · or build from source:
cargo build --release -p tq-cli
tq init MyGame
echo "asset" > Content/hero.txt
tq commit -m "first asset"
tq log        # ch-4f2a91  rev:1  first asset  (you)
tq status     # what changed since the last commit

Made a mistake? Nothing is lost:

tq restore    # back to the last committed state
tq undo       # ...and if restore itself was the mistake, get your work back
This is not an editor-style undo: TouchQuill snapshots your working state into storage before every operation, so undo can bring back even uncommitted files.

Working with a team

tq clone https://vcs.studio.com MyGame     # fresh clone
tq login --user ola                          # prompts for a password (set by your admin)
tq push
tq pull

You log in like a person: username and password. Under the hood the server exchanges the password for a session token you never see; explicit tokens (tq login tqt_…) remain for CI and scripts. Your identity on the server comes from the login, never from what the client claims, which is why nobody can impersonate a teammate in history or in the audit log. Push is always fast-forward: if someone got there first, pull first.

Prefer clicking? TouchQuill Studio, the graphical client (Windows/Linux/macOS), walks you through the same thing with a wizard: server address → username and password → pick a project → download a working copy with a progress bar. Plus views for changes, history, locks, conflicts and Lens. And if you live in your editor, there's a VS Code extension too (status, locks, commit and Lens history right from the code window).

Artists: it's all clicks in Studio

An artist doesn't need to know a single command. The whole day is three clicks in the Studio app, and everything that happens "under the hood" below takes care of itself.

1. Sync

Open Studio and click Sync. It pulls the team's latest state. If the studio tracks builds, one toggle grabs the last stable revision instead of the very latest, one that's guaranteed to open in the editor.

2. Take the file you'll edit

Before you touch a file, select it and click Reserve. From that moment it's yours alone and no one can overwrite it. If someone's already holding it, you see who, and one click puts you in the queue; Studio notifies you the moment it frees up.

3. Send your changes

Done? Type a short description and click Send. Studio saves the change, uploads it to the server and releases the reservation, all in one move. The next person in the queue gets the file automatically.

Interrupted mid-work? Park the unfinished change with the Save for later button, no clutter in the history, and come back to it whenever you like. Every one of these clicks is, underneath, the same operation a programmer runs with tq commands (see below), the artist just never has to see them.

Programmers

A channel per task

A task channel inherits from main, but your commits don't affect anyone until you're done.

tq channel create task/GROM-447 --type task
tq channel switch task/GROM-447

Commit freely, fix boldly

amend folds a fix into the last commit, reword edits the message. The Change ID stays the same, so nothing gets lost.

tq commit -m "dodge system"
tq amend                    # fix-up on the same change

Stay current with main

integrate pulls in the parent's changes. A conflict won't stop you: it gets recorded, and you decide when and how to resolve it.

tq integrate
tq conflicts                # what's pending
tq conflict resolve cfl-a1 --take theirs

Close the task

complete merges the channel into its parent and closes it. The task's history stays readable.

tq complete

Managers: releases and order

A release channel with strict rules

No commits with unresolved conflicts land on a release, and only a chosen few can push. The policy lives in a versioned file: changing it goes through history like everything else.

tq channel create release/1.0 --type release
# tq-channel.toml:
[permissions]
write = ["lead", "server-admin"]
integrate = ["lead"]

Lock disputes

Someone left a lock and went on vacation? Request a hand-over, or take it if you must. Every such action lands in the audit log, with the reason.

tq lock --request Content/Boss.uasset
tq steal Content/Boss.uasset     # requires lock.steal

Review before you close

A channel can require approvals before complete goes through. Reviewers comment and approve/reject, from the CLI or the web panel. An approval is tied to a specific state: a new push invalidates old approvals, so nobody sneaks changes past review.

# tq-channel.toml:
[review]
require-approvals = 2
# reviewer:
tq review approve
tq review comment "rename this variable" --path Combat.cpp --line 42

Conflicts under control

Conflicts can be assigned to people and given deadlines. Overdue ones escalate on their own: nothing drowns in the noise.

tq conflict assign cfl-a1 marta
tq conflict defer cfl-b2 --deadline 48h

Who did what

The audit log records administrative actions and permission-denied attempts. Verifying its integrity is one command.

tq-server audit log --denied
tq-server audit verify

CI and builds

An account with exactly the rights it needs

CI gets a PAT token limited to specific permissions: even if it leaks, it can't do anything beyond its scope.

tq-server user pat ci-build --user ci --scopes repo.read,build-flag.set

Build and mark

After a successful build, CI flags the revision. That flag means "this version works".

tq build-flag set --rev rev:128 --flag ci --state ok

The team takes stable

An artist never has to land on a programmer's broken commit: get stable fetches the newest revision with all required flags green.

tq get stable

Channels

A channel is a line of work: the counterpart of a branch, but with a type and a policy. The type carries sensible defaults: a release rejects conflicted commits, a task has an owner and a lifecycle (create → integrate → complete), personal is your sandbox.

The virtual channel is worth knowing: a view onto a slice of the repo. Your art team can get just Content/: as far as they're concerned the code doesn't exist, yet their commits land in shared history with the invisible files preserved.

tq channel create artview --type virtual --include "Content/..." --exclude "Source/..."

For working on a slice of a large repo there's also sparse checkout: you fetch and materialize only the matching paths, the rest never touches your disk. A character artist doesn't have to pull every map.

tq clone https://vcs.studio.com Game --sparse "Content/Characters/...,Content/Shared/..."
tq sparse set "Content/Characters/..."   # narrow/widen in an existing clone

Locks

The model is simple: one owner, everyone else queues. Locks have an inactivity timeout (4 h by default). If you forget to release one and go home, the team isn't blocked overnight. You can ask the owner to hand a lock over (--request) or pass yours to a specific person (--give).

The server enforces locks at push: if the channel policy says *.uasset requires a lock, a push touching such a file without one is rejected with a clear message. Offline work is covered too: declare a lock locally, sync when you're back; if two people took the same file offline, a dispute blocks both sides until a lead decides. Nobody silently overwrites anyone's work.

Conflicts

In most systems a conflict is a wall: the merge stops and you drop everything to defuse it. In TouchQuill a conflict is a stored object (cfl-…) with full context: both versions, the common ancestor, who and when. Integration always completes; you resolve conflicts when you have room for them, or a lead delegates them.

tq conflict resolve cfl-a1 --take theirs   # accept their version
tq conflict resolve cfl-a1 --merge         # three-way merge via plugin (if the format has a differ)

Change ID

A commit hash changes with every fix-up; a Change ID doesn't. The distinction sounds subtle, but in practice it means a review comment saying "fix this in ch-4f2a91" stays valid after your amend and rebase. The operation log (tq op log) completes the picture: every operation leaves a trace and most can be undone.

Lens

Classic blame answers "who changed line 40 of file X". Lens answers the question you actually have: "what happened to this function?", even when the file was renamed and the function moved between modules. That includes Unreal Blueprints (functions, events and macros from .uasset files). Cosmetic edits (formatting, comments) don't pollute the result.

tq lens blame --symbol calculate_damage
CREATED       ch-6f9  rev:1   krzysztof  "first version"
MODIFIED      ch-a12  rev:3   marta      "damage buff"
MOVED+RENAMED ch-c73  rev:5   krzysztof  (compute_damage from Weapon.rs)  [~86%]

Symbols come from a real parser (tree-sitter) for C++, C#, C, Rust, Python, Lua, Go, Java, JS/TS, with class methods and qualified names (Enemy::TakeDamage), plus a fallback extractor for Verse (UEFN), GDScript and shaders. For binary formats there is a plugin mechanism (a WASM host), and Blueprint support is on the development roadmap. Matching is fuzzy: a rename together with a body edit gets a confidence score ([~86%]), and a move spread across several commits within a week is stitched into one history. When the heuristic is unsure, it flags the result as ambiguous: you resolve it with tq lens link, or with one click in Studio. tq lens trace shows the full life of a symbol across every name it ever had. Full Lens writeup: languages, matching and limits →

Lens in the editor. The VS Code extension shows a CodeLens "Lens: name" above every function and class. Click it to open its history. Same from the context menu on the symbol under the cursor. Powered by tq lens blame --json. On the roadmap: Lens for Blueprints (symbols in graphs and .uasset files) and plugins for full Visual Studio and JetBrains Rider.

Best practices

A few habits that make the biggest difference in practice:

Migrating from Git and Perforce

Both migrations preserve history: authors, timestamps, messages. Change IDs are derived deterministically from the source, and Lens works on imported history from day one. A sensible order: migrate a copy, verify it, switch the team over, and keep the old repo read-only for a transition period.

tq migrate from-git --repo /path/to/repo --branch main
tq migrate from-p4  --port perforce:1666 --user krzysztof --path //depot/...
tq migrate verify   --repo /path/to/repo

Moving a big studio doesn't have to be a single jump. After the first import, tq migrate p4-sync pulls only the new changelists from Perforce: during the migration P4 stays the source of truth and TouchQuill mirrors it.

Server and permissions

The server exposes gRPC (clients), REST (integrations) and SSE (a live event feed). Authentication switches on with the first user you add: before that it runs in open mode, handy for trying things out.

tq-server --data /var/tq --listen 0.0.0.0:7470 --tls-cert cert.pem --tls-key key.pem
tq-server user add krzysztof --role developer
Browser admin panel, a P4Admin equivalent built into the server: http://server:7480/admin. Log in with a password and manage users (add, roles, passwords, PATs), browse repositories (revision history, channels, locks with queues and conflicts) and the audit log with one-click hash-chain verification. There's also a repository browser (the file tree of any revision, content preview and diffs) available to anyone with read access, no cloning required. Same permissions as the CLI; every change lands in the audit log.

Seven built-in roles (from viewer to server-admin), custom roles cloned with permissions subtracted, path restrictions for contractors ("only Content/Weapons/..."), a Perforce-style protections table (rules per repo × path, e.g. "artists can't write to Content/Code/..."). Read rules apply on the wire too: a file you're not allowed to read simply never reaches your disk. PATs with scopes for automation, per-channel overrides, and, for password login, a complexity policy, lockout after repeated failures and optional 2FA (TOTP).

Scale is handled gradually, without a rewrite. A small studio starts on a single file (SQLite) and a daily directory copy. A larger one switches metadata to PostgreSQL (one environment variable; CAS data stays put), and a remote office runs an Edge Proxy: a read-through object cache on the local network. Add regional replication (locks stay on the primary, one source of truth), server-to-server mTLS, one-command backups, and cryptographically signed releases.

Command reference

CommandDescription
tq init / clone / setupcreate / clone / configure a repo
tq add / edit / status / diffworktree changes
tq commit / amend / rewordrecord a revision (stable Change ID)
tq log / show / cat / restorehistory and navigation
tq rebase / revertrebase / inverse revision
tq op log / undo / redooperation log and undo
tq channel …create / switch / list / policy
tq integrate / completeintegrate and close a channel
tq lock / unlock / locksqueue-based locks, request / give
tq conflict(s) …resolve / assign / defer
tq shelf …shelve work, share, TTL
tq lens blame / trace / symbolssymbol history and full lineage
tq lens link / reindex / indexmanual stitch, rebuild and top-up of the index
tq review comment / approve / rejectcomments and approvals (gate on complete)
tq sparse set / show / clearwork on a slice of the repo
tq renamerename the repository
tq build-flag / get stablebuild flags and the stable revision
tq workspace …save / sync / offline / mount / presence
tq migrate from-git / from-p4 / p4-synchistory import and incremental P4 mirror
tq fsmonitor start / stop / statusfile-change daemon (Linux / Windows / macOS)
tq notify …inbox, DND, digests
tq plugin …WASM plugins: extractor / differ
tq admin obliterate / channelpermanent content removal, permission overrides

The full technical specification and architecture decision records (ADRs) are available on request.