Wiki
CLI uversion
Complete uVersion CLI reference: every command, flag, example, JSON output, exit codes.
The uversion binary covers the same versioning operations as the desktop client,
plus a --json output a program can consume, for automation:
continuous integration, onboarding scripts, in-house tooling. This page is aimed at developers and
studio administrators.
Installation
On Windows, the uversion.exe binary is included in the desktop
client installer and added automatically to the user PATH (%LOCALAPPDATA%\uVersion).
The PATH entry is deduplicated on every update and removed on uninstall.
Open a new terminal and type:
uversion --help
On macOS, it is automatic: on first launch, the desktop client creates a link
to the embedded binary at ~/.local/bin/uversion and makes sure that folder is on
the PATH (via ~/.zprofile). Launch the app once, open a
new terminal, and the uversion command is available.
To do it by hand to another location on the PATH:
ln -s /Applications/uVersion.app/Contents/Resources/uversion /usr/local/bin/uversion
On Linux, it is automatic too, exactly as on macOS. The desktop client
is distributed as an AppImage, and that AppImage contains the uversion binary.
On first launch, the app places it at ~/.local/bin/uversion and makes sure
that folder is on the PATH. Launch the app once, open a
new terminal, and the command is available:
uversion --help
You have nothing to compile. On Linux, the desktop client is distributed only as an AppImage, and
that is what brings the CLI. To place
the binary elsewhere on the PATH, copy it from ~/.local/bin:
sudo cp ~/.local/bin/uversion /usr/local/bin/uversion
To check the installed version:
$ uversion --version
Cheatsheet: every command
The full set of available commands, in the order you typically encounter them:
| Command | What it does |
|---|---|
uversion login <url> -u <user> | Authenticate against a server |
uversion logout | Clear the stored credentials |
uversion repos | List accessible repositories |
uversion clone <repo> [path] | Clone a repository |
uversion info | Show the workspace state + current user |
uversion status [paths...] | See modified / new / deleted / locked files |
uversion checkout <paths...> | Reserve files for editing (acquires a lock) |
uversion checkin <paths...> -m "..." | Upload and commit changes |
uversion revert <paths...> | Discard local changes and release the locks |
uversion sync | Fetch changes from the server (whole repository) |
uversion content <path> --revision <n> | Download a specific version of a file |
uversion log | Commit history |
uversion lock list | See all locks in the repository |
uversion lock release <paths...> | Release a lock without touching the file |
uversion lock heartbeat | Signal that your locks are still in use (for monitoring) |
uversion trust <url> | Remember a server's self-signed certificate fingerprint (interactive; --yes to script it) |
uversion mistrust <url> | Forget a server's remembered fingerprint |
uversion trusted | List servers whose fingerprint is remembered |
Every command except uversion content accepts --json, which replaces
the human-readable display with output a program can consume (see
JSON output). Every command accepts --help for the option
details.
checkout, checkin and revert also accept
--paths-file <fichier>, indispensable as soon as you work at the scale
of an Unreal project. See Large path lists.
Authentication
uversion login
Authenticates against a uVersion server. The server hands back a session token (a JWT, for JSON Web Token), which the CLI stores in the system password manager: Credential Manager on Windows, Keychain on macOS, libsecret on Linux. This token is shared with the desktop client, the Unreal plugin and the Rider plugin: signing in on one side signs in the others, and signing out signs them all out.
uversion login <url_serveur> -u <utilisateur> [-p <mot_de_passe>]
| Option | Description |
|---|---|
-u, --username | Username |
-p, --password | Discouraged. See the box below. If the option is omitted, the CLI reads the UVERSION_PASSWORD environment variable, and failing that prompts for the password interactively, with no echo to the screen. |
UVERSION_PASSWORD, never -p
On a multi-user system, each process's command line is readable by the other accounts on the
machine: ps on Linux and macOS, Task Manager or wmic on Windows. A
password passed as an argument shows up there in clear text, even when it comes from a variable,
since the shell replaces it with its value before launching the program. It also ends
up in the shell history and often in the continuous-integration logs.
The CLI reads UVERSION_PASSWORD precisely to avoid this, and prints a warning to
standard error when -p is used.
Examples:
$ uversion login https://uversion.mygamestudio.com -u alice
Password:
Logged in as alice (artist)
# Sur un serveur de studio, en interne. Le port par défaut est 8443, en HTTPS.
$ uversion login https://192.168.1.100:8443 -u bob
Password:
Logged in as bob (programmer)
# Compte d'intégration continue : le mot de passe passe par l'environnement,
# jamais par la ligne de commande.
$ export UVERSION_PASSWORD="$SECRET_FROM_VAULT"
$ uversion login "$UV_SERVER" -u ci-nightly
Logged in as ci-nightly (programmer)
On the first connection to a server that presents a self-signed certificate, the CLI shows the
certificate fingerprint and asks you to confirm it, then remembers it. This is the principle of
trust on first use, the same as SSH: you accept an identity once, and any later
presentation of a different identity is flagged. If the fingerprint changes, the CLI refuses the
connection and warns you: it is either a legitimate certificate renewal, or an
interception of your traffic by a third party. The deliberate move is then
uversion mistrust <url>, followed by a new connection.
uversion logout
Erases the token from the system password manager and invalidates, on the server, every session for this account. Since the token is shared, this also signs out the desktop client, the Unreal plugin and the Rider plugin, on all your machines.
$ uversion logout
Logged out (alice)
$ uversion logout # si aucune session n'était ouverte
Already logged out
Which account acts? The workspace's, not the last one signed in
This is the point that surprises people most, and you had better know it before scripting anything: the identity used is not that of the last login, it is that of the workspace you are in.
A workspace is a cloned folder, recognizable by its .uversion subfolder. The
.uversion/config.toml file records the server and the owner account there:
[repository]
id = "1"
name = "hero-rpg"
server_url = "https://uversion.mygamestudio.com"
[workspace]
id = "..."
name = "alice-cli"
owner = "alice"
last_synced_revision = 42
As soon as you are inside a workspace, server_url and owner are
authoritative: status, checkout, checkin, revert,
sync, log, content, lock and info
authenticate as owner, against server_url.
Only login, logout, repos and
clone use the configuration shared across all workspaces
(%APPDATA%/uversion/uVersion/config/config.toml on Windows), which follows the active
account of the desktop client.
Why. One machine often serves several accounts, for instance a freelancer who
works for two studios. Without this rule, every workspace would act under whichever account is
active at the moment: a folder cloned by alice but used while bob is
active would place its locks under bob's name, would see its own files as "locked by
someone else", and would have its submissions refused.
To change a workspace's identity, edit the owner field in
.uversion/config.toml, and make sure that account has signed in at least once on this
machine (uversion login), so that its token is present. Then check with
uversion info, which shows the account actually used.
The token is stored under the username alone, without the server. If the same name exists on two different uVersion servers, both share one and the same entry: signing in to the second replaces the first's token. Use distinct names, or use only one server at a time from a given machine.
Repositories
uversion repos
Lists the repositories the account has access to. This command uses the shared configuration, not a workspace's: it therefore answers for the account of your last login.
$ uversion repos
ID Name Description
----------------------------------------------------------------------
1 hero-rpg Main RPG project
2 shared-assets Shared asset library
12 prototype-fps R&D prototype FPS
$ uversion repos # si aucun dépôt n'est accessible
No repositories found
uversion clone
Fetches a repository locally. If the path is omitted, a folder named after the repository is
created in the current directory. The clone also creates the .uversion subfolder,
which turns the folder into a workspace and records the server and the owner account.
The transfer applies deduplication: the content is split into blocks, and a block already present is stored only once, even if it appears in several files. That is why the space used on disk is often noticeably smaller than the volume downloaded.
uversion clone <repo_name_or_id> [local_path]
Examples:
$ uversion clone hero-rpg
Cloning hero-rpg to ./hero-rpg...
✓ 8,432 files in 47s (14.2 GB downloaded, 6.1 GB on disk after dedup)
$ uversion clone hero-rpg D:\Projects\HeroRPG
$ uversion clone 1 # par ID au lieu du nom
Files
uversion status
Shows the state of the files in the current workspace: modified, new (untracked), deleted, locked by others.
uversion status [paths...] [--json]
Examples:
$ uversion status
Modified:
M Content/Maps/MainLevel.umap (locked by alice)
New:
A Content/Textures/NewTexture.png
Deleted:
D Content/OldAsset.uasset
Locked by others:
L Content/Characters/Hero.uasset (locked by bob)
1 modified, 1 new, 1 deleted, 1 locked by others
$ uversion status Content/Maps # filtre par dossier
$ uversion status --json | jq '.summary' # extraction scriptable
uversion checkout
Places an exclusive lock on the target files and makes them writable on disk. Tracked files are read-only until they are reserved: this is what prevents two people from editing the same binary asset in parallel.
uversion checkout <paths...> [--paths-file <fichier>] [--force] [--add] [--json]
| Option | Description |
|---|---|
--paths-file | Read additional paths from a file, one per line. See Large path lists. |
--force | Takes the lock even if it is held by someone else. Restricted to accounts that hold the force_unlock capability, that is, the admin and lead roles. See below. |
--add | Allows reserving paths not yet present locally, for new files. |
--force really does
The option takes someone else's lock. It is guarded by the force_unlock capability,
held by the admin and lead roles. An account that does not have it
receives an explicit refusal, with the recourse to follow: ask an
administrator, or use the "Request release" button in the desktop client, which notifies the
person concerned.
When a takeover has actually happened, it is recorded in the audit log, with the paths concerned and the names of the people whose locks were taken. Nothing is written if the option was present but no one else's lock changed hands: in scripts, the flag is often applied systematically, and a log full of pointless events is a log nobody reads.
Examples:
$ uversion checkout Content/Maps/MainLevel.umap
✓ Lock acquired: Content/Maps/MainLevel.umap
$ uversion checkout Content/Characters/Hero.uasset Content/Characters/Villain.uasset
✓ Lock acquired: Content/Characters/Hero.uasset
✓ Lock acquired: Content/Characters/Villain.uasset
# Fichier déjà réservé par bob
$ uversion checkout Content/Maps/MainLevel.umap
✗ File is locked (bob)
# Compte sans la capacité force_unlock
$ uversion checkout --force Content/Maps/MainLevel.umap
Error: Taking a lock held by another user requires the force_unlock capability
(admin or lead). Ask an administrator, or use Request Release to ask the holder.
# Compte admin ou lead : le vol passe, et il est tracé
$ uversion checkout --force Content/Maps/MainLevel.umap
✓ Lock acquired: Content/Maps/MainLevel.umap
uversion checkin
Uploads the modified files and commits them to the server in a single transaction: either everything goes through, or nothing does. The locks are released automatically on success.
uversion checkin [paths...] [--paths-file <fichier>] -m <message> [--all] [--json]
| Option | Description |
|---|---|
-m, --message | Commit message. Required. |
-a, --all | Include every modified file in the workspace, not only the ones passed as arguments. |
--paths-file | Read additional paths from a file, one per line. See Large path lists. |
Examples:
$ uversion checkin Content/Maps/MainLevel.umap -m "Fixed lighting in main level"
Validating 1 file...
✓ All validation rules passed
Uploading: [####################] 100% · 84 MB
✓ Committed as 7f3a9b1 (1 file, 84 MB uploaded, 0 deduped)
$ uversion checkin --all -m "Weekly art update" # tout le workspace
$ uversion checkin Content/Characters/ -m "Updated character meshes"
uversion revert
Discards the local changes to one or more files, restores the server version, and releases the matching locks.
uversion revert <paths...> [--paths-file <fichier>] [--json]
Examples:
$ uversion revert Content/Maps/MainLevel.umap
✓ Reverted: Content/Maps/MainLevel.umap (lock released)
$ uversion revert Content/Characters/ # récursif par dossier
Large path lists: --paths-file
checkout, checkin and revert accept
--paths-file <fichier>: a text file containing one path per
line. The paths read this way are added to those passed as arguments, they do not replace
them.
What it is for. On an Unreal project, an operation commonly covers several
thousand files. Passing them all as arguments runs into a system limit: on Windows, a command line
cannot exceed 32,767 characters, which is about 500 asset paths. Beyond that, the command fails
before it even starts, with a system error message that says nothing about the real problem.
--paths-file removes this limit: the file can contain as many as needed.
Reserve every modified file in a folder, whatever their number:
$ uversion status --json \
| jq -r '.files[] | select(.status == "modified") | .path' > /tmp/changed.txt
$ wc -l /tmp/changed.txt
3184 /tmp/changed.txt
$ uversion checkout --paths-file /tmp/changed.txt
Then submit exactly the same batch:
$ uversion checkin --paths-file /tmp/changed.txt -m "Import de la passe d'éclairage"
On Windows, in PowerShell:
PS> (uversion status --json | ConvertFrom-Json).files |
Where-Object { $_.status -eq "modified" } |
ForEach-Object { $_.path } |
Set-Content -Encoding utf8 changed.txt
PS> uversion checkout --paths-file changed.txt
This is also the mechanism the Rider plugin uses to pass a large changelist.
uversion sync
Downloads the latest changes from the server and applies them to the local workspace.
uversion sync [--force] [--json]
| Flag | Description |
|---|---|
-f, --force | Full sync: re-downloads every file, not only the delta since the last sync. Useful when the workspace is corrupted. |
Examples:
$ uversion sync
Syncing from revision 41 → 47...
✓ 12 files updated, 3 added, 1 deleted (1.4 GB downloaded)
$ uversion sync --force # re-télécharge tout
uversion content
Downloads a specific version of a file without touching the local workspace. Useful to compare,
archive, or retrieve a past state without doing a revert.
uversion content <path> [-r <numéro_de_révision>] [-o <fichier>]
--revision expects an integer, not a commit hash
It is the file's revision number: a counter that is 1 at its first version, 2
at the second, and so on. Passing a commit hash like 6e2b8a0 makes the command fail
at argument parsing.
The number can be read in uversion log --path <fichier>, where each file line
shows it in parentheses. Omitting --revision downloads the latest version.
Examples:
$ uversion content Content/Maps/MainLevel.umap --revision 12 --output ./snapshot.umap
$ uversion content Content/Characters/Hero.uasset -r 8 -o ./hero-v8.uasset
# Sans --output, le contenu est écrit sur la sortie standard
$ uversion content Config/DefaultEngine.ini -r 3 > DefaultEngine-v3.ini
History
uversion log
Commit history of the current repository, optionally filtered by file.
uversion log [-n <limit>] [-p <path>] [--json]
| Flag | Description |
|---|---|
-n, --limit | Number of entries to display (default: 20) |
-p, --path | Filter by file path |
Examples:
$ uversion log
commit 7f3a9b1c2d...
Author: alice
Date: 2026-05-15 08:30:00 UTC
Fixed lighting in main level
Content/Maps/MainLevel.umap (rev 12)
commit 6e2b8a0...
Author: bob
Date: 2026-05-14 17:22:00 UTC
Hero pose pass
Content/Characters/Hero.uasset (rev 8)
Content/Characters/OldHero.uasset (deleted, rev 9)
$ uversion log -n 5 # 5 derniers commits
$ uversion log --path Content/Maps/MainLevel.umap # historique d'un fichier
A line marked deleted is a deletion revision: it carries a number like the others, but
has no content to download.
Locks
It holds until it is explicitly released: by uversion checkin, by
uversion revert, by uversion lock release, or by an administrator's
force-unlock. No automatic expiry exists, neither after an hour nor after a
month. A file reserved and forgotten stays that way until someone intervenes.
Consequently, uversion lock heartbeat extends nothing. This command
only says "these locks are still useful to me", so that administrators can tell an active lock
from an abandoned one.
uversion lock list
Shows all the locks in the current repository.
uversion lock list [--json]
Examples:
$ uversion lock list
File User Acquired
----------------------------------------------------------------------
Content/Maps/MainLevel.umap alice 2026-05-15T08:42:11Z
Content/Characters/Hero.uasset bob 2026-05-14T17:00:00Z
Content/UI/HUD.uasset alice 2026-05-15T09:15:00Z
If there is nothing to show, the command writes No active locks.
uversion lock release
Releases one or more locks without touching the local content of the file. Use it to "hand back" an asset you have not modified: reserved by mistake, or work abandoned without submitting.
uversion lock release <paths...> [--json]
Examples:
$ uversion lock release Content/Maps/MainLevel.umap
✓ Lock released: Content/Maps/MainLevel.umap
This command releases only your own locks. To remove someone else's, you have to
go through administration, or through uversion checkout --force if you are
admin or lead.
uversion lock heartbeat
Signals that the locks held by the current account are still in use. This does not extend them: nothing expires. It is a monitoring signal, so that an administrator inspecting the lock list can see which ones are still active. Useless in day-to-day work; useful for an automated job that keeps a file reserved for hours.
$ uversion lock heartbeat
3 lock(s) extended
$ uversion lock heartbeat # si vous ne détenez aucun verrou
No locks to extend
Example in continuous integration:
$ while build_in_progress; do
uversion lock heartbeat
sleep 300
done
Info
uversion info
Shows the account used and the state of the current workspace. It is the command to run first when something behaves unexpectedly: it shows under which identity the CLI actually acts, which is the workspace owner's and not necessarily that of your last login (see Which account acts?).
$ uversion info
User: alice (lead)
Repository: hero-rpg (id: 1)
Server: https://uversion.mygamestudio.com
Workspace: alice-cli (3f2a1c8e-...)
Local path: D:\Projects\HeroRPG
Last sync: revision 42
Outside a workspace, or without a valid session:
$ uversion info
User: not logged in
Workspace: not in a uVersion workspace
The command does not count the files and does not show a summary of the changes: that is
uversion status's job.
JSON output
Every command except content accepts --json, which replaces the
human-readable display with structured output. This is what makes the CLI scriptable.
log, lock list and repos produce a
JSON array directly, with no wrapping object. So there is no commits
key, no locks key, no repositories key: it is .[] you must
write in jq, not .commits[]. An expression targeting a nonexistent key
produces nothing at all, with no error message.
Example: uversion status --json
{
"files": [
{
"path": "Content/Maps/MainLevel.umap",
"status": "locked",
"locked_by": "alice",
"is_owned": true,
"version": 12
},
{
"path": "Content/Textures/NewTexture.png",
"status": "new",
"locked_by": null,
"is_owned": false,
"version": 0
}
],
"summary": {
"modified": 1,
"new": 1,
"deleted": 0,
"locked_by_others": 0
}
}
The possible values of status:
| Value | Meaning |
|---|---|
modified | The file is writable on disk, with no lock placed |
locked | Reserved by you |
locked_other | Reserved by someone else, named in locked_by |
new | Present locally, unknown to the server |
deleted | Present on the server, absent locally |
tracked | Tracked and intact. Appears only if you filtered by path |
Note that summary.modified sums modified and locked, since
both denote a file you are working on.
Example: uversion log --json -n 1
[
{
"commit_hash": "7f3a9b1c2d...",
"message": "Fixed lighting in main level",
"author": "alice",
"created_at": "2026-05-15T08:30:00Z",
"files": [
{
"path": "Content/Maps/MainLevel.umap",
"revision_number": 12,
"file_size": 84934656,
"is_delete": false
}
]
}
]
Example: uversion lock list --json
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"file_id": 12345,
"file_path": "Content/Maps/MainLevel.umap",
"user_id": 12,
"username": "alice",
"acquired_at": "2026-05-15T08:42:11Z",
"expires_at": "2126-05-15T08:42:11Z"
}
]
The 2126 expiry is not a typo: the column cannot stay empty in the database, so the server writes a value pushed a hundred years out. Locks do not expire. Do not show this field to a user and do not build anything on it.
Error handling
On failure, the CLI writes Error: <message> to standard
error and exits with code 1. Errors are never emitted as JSON on standard output: with
--json, only a success result is structured. In a script, test the exit code, not the
output content.
Common patterns
Onboarding a new team member
uversion login https://uversion.mygamestudio.com -u newdev
uversion repos # confirme l'accès
uversion clone hero-rpg ~/Projects/HeroRPG # download initial
Daily workflow (artist / programmer)
# Début de journée
uversion sync
# Avant d'éditer
uversion checkout Content/Maps/MainLevel.umap
# ... édition dans Unreal Editor ou Rider ...
# Commit en fin de journée
uversion checkin --all -m "Updated main level + hero animations"
Audit script: who reserved what?
lock list --json returns an array at the root. So you iterate with .[],
and the fields are username, file_path and acquired_at:
uversion lock list --json | jq -r '.[] | "\(.username)\t\(.file_path)\t\(.acquired_at)"'
The files reserved by a given person:
uversion lock list --json | jq -r '.[] | select(.username == "bob") | .file_path'
Extract the commit hashes
Here too the array is at the root, and the field is called commit_hash:
uversion log --json -n 50 | jq -r '.[].commit_hash'
One person's commits, with their message:
uversion log --json -n 200 \
| jq -r '.[] | select(.author == "alice") | "\(.commit_hash[0:8]) \(.message)"'
Retrieve an asset at a past revision, without touching the workspace
uversion content expects a revision number, not a commit hash. Find
it in the file's history, where it is shown in parentheses:
$ uversion log --path Content/Characters/Hero.uasset -n 10
commit 6e2b8a0...
Author: bob
Date: 2026-05-14 17:22:00 UTC
Hero pose pass
Content/Characters/Hero.uasset (rev 8)
$ uversion content Content/Characters/Hero.uasset --revision 8 --output ~/backup/Hero-v8.uasset
Or in one go, for the latest revision of a file:
REV=$(uversion log --json --path Content/Characters/Hero.uasset -n 1 \
| jq -r '.[0].files[] | select(.path == "Content/Characters/Hero.uasset") | .revision_number')
uversion content Content/Characters/Hero.uasset --revision "$REV" --output ./Hero.uasset
Nightly build in continuous integration
The password goes through UVERSION_PASSWORD, never through -p: a
process's command line is readable by the other accounts on the machine.
export UVERSION_PASSWORD="$SECRET_FROM_VAULT"
uversion login "$UV_SERVER" -u ci-nightly
unset UVERSION_PASSWORD
uversion clone hero-rpg ./project
cd project
uversion sync --json > sync.log
# Réserver un fichier pour la durée du cook, et signaler qu'il sert toujours
uversion checkout Content/Cooking/Distribution.uasset
( while pgrep RunUAT; do uversion lock heartbeat; sleep 300; done ) &
# ... build et cook ...
uversion lock release Content/Cooking/Distribution.uasset
Environment variables & exit codes
Environment variables
| Variable | Description |
|---|---|
UVERSION_PASSWORD |
Password used by uversion login when the -p option is absent.
This is the recommended way to automate a login: unlike a command-line
argument, an environment variable is not exposed to the other accounts on the machine. If it
is empty or absent, the CLI prompts for the password interactively.
|
RUST_LOG |
Log verbosity, written to standard error. For example RUST_LOG=debug. Default
level: warn.
|
No UV_* variable is read. The server and the account come from
.uversion/config.toml when you are inside a workspace, and otherwise from the shared
configuration (%APPDATA%/uversion/uVersion/config/config.toml on Windows). The session
token comes from the system password manager. See
Which account acts?.
Exit codes
| Code | Meaning |
|---|---|
0 | Success. It is also the code returned by --help and --version, which are not errors. |
1 | Any application error: authentication, permission, network, server, disk write, outside workspace, conflict, validation. The CLI does not distinguish the causes by exit code; the detail is on standard error. |
2 | Argument parsing error: unknown option, missing value, invalid subcommand. |
Example in a shell script:
if ! uversion checkin --all -m "Nightly"; then
echo "Checkin failed, see stderr"
exit 1
fi