The Revision Model¶
This page explains how FlexVault identifies revisions, how draft and published revisions relate to each other, and what happens to your work when you publish, sync, or move the workspace to an earlier point. If you only want the high-level picture, the Introduction covers it in a few paragraphs.
Revision IDs¶
Every revision, whether a draft revision or a published revision, is identified by a revision ID. Each ID is made up of up to three parts separated by a . character: the branch name, the published revision number, and, if present, the draft revision number.
Quick reference
- Published revisions look like
branch_name.number, for examplemain.123ormy_feature.4 - Published revisions are shared across the team.
- Draft revisions look like
branch_name.number.draft_number, for examplemain.123.4ormy_feature.4.2. - Draft revisions are local to your workspace only.
- The draft revision
.0is a special alias that always points to the published revision it is based on, somain.123.0is an alias formain.123. - Published revision numbers count up from
0, so a branch's first publish isbranch_name.0.
Drafts are local, published revisions are shared¶
A published revision ID identifies the same content in every workspace connected to the repository. main.457 refers to one particular publish to main, and it is that same set of files for everyone, which is what makes it safe to send to a colleague, quote in a bug report, or hand to a build machine.
Draft revision IDs work differently. A draft exists only in the workspace that created it, and its draft number counts that workspace's own snapshots. Two people working on top of main.456 will each produce a main.456.1, and those are unrelated pieces of work that happen to share an ID.
A branch follows the same rule. One created with fxv branch new exists only in the workspace that created it, and holds nothing but drafts, until its first fxv publish makes both the branch and that revision visible to everyone.
Working in a new workspace¶
Say you are working in the main branch. The most recent publish was main.456. When you create a workspace and sync it, your workspace pulls in the files from main.456 and creates a local draft revision on top of it, main.456.0.
As you make changes, snapshot your work with fxv snapshot to create new draft revisions as you go: main.456.1, then main.456.2, and so on. Game engine integrations (Unity, Godot, and Unreal Engine) can also create these draft snapshots automatically around major edits and in the background.
---
title: "Draft revisions accumulating on top of `main.456`"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.454"
commit id: "main.455"
commit id: "main.456"
branch main.456-drafts
checkout main.456-drafts
commit id: "main.456.0"
commit id: "main.456.1"
commit id: "main.456.2" tag: "Head"
Publishing¶
When your work is ready, fxv publish takes your current draft revision, main.456.2, and publishes it to the repository as a new published revision, main.457. Your workspace is updated to match, and your current draft becomes main.457.0.
Note
fxv publish requires you to be logged in as a user, see fxv login. Snapshotting your work as you go does not require this.
---
title: "Publishing your draft revision as `main.457`"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.454"
commit id: "main.455"
commit id: "main.456"
branch main.456-drafts
checkout main.456-drafts
commit id: "main.456.0"
commit id: "main.456.1"
commit id: "main.456.2"
checkout main
merge main.456-drafts id: "main.457"
branch main.457-drafts
checkout main.457-drafts
commit id: "main.457.0" tag:"Head"
Syncing and merging¶
If someone else publishes to main while you are working, for example publishing main.458, you bring their work into your workspace with fxv sync. If you have unpublished draft revisions, FlexVault merges them on top of the latest published revision as a new draft, so your in-progress work is preserved.
---
title: "Syncing drafts from `main.457` onto `main.458`"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.457"
branch main.457-drafts
checkout main.457-drafts
commit id: "main.457.0"
commit id: "main.457.1"
commit id: "main.457.2"
checkout main
commit id: "main.458"
branch main.458-drafts
checkout main.458-drafts
merge main.457-drafts id: "main.458.1" tag: "Head"
If any of your draft changes conflict with what was published, sync completes with those files flagged as conflicts, and you resolve them with fxv resolve before you can publish. See Merging and Conflict Resolution for how the merge itself works.
From here you continue as normal, and publishing again produces main.459.
Branches¶
Every revision ID begins with a branch name, so every revision belongs to exactly one branch and stays associated with it for good. Published revision numbers are counted per branch and run independently of each other: the numbers on main count publishes to main, and those on alice/shaders count publishes to alice/shaders, with no relationship between the two sequences.
A global branch has a bare name, like main, and belongs to nobody. A user branch is written with its owner's username as a prefix, like alice/shaders. Names are unique per owner rather than across the repository, so alice/shaders, bob/shaders and a global shaders are three separate branches with three separate sequences of revisions.
Starting a branch¶
fxv branch new starts a branch from an existing revision. The new branch has nothing published on it, so there is no published revision number for its first draft to sit on top of, and a - is written in that position instead. A branch created from main.456 therefore begins at alice/shaders.-.1, and drafts accumulate from there as they do anywhere else.
The first fxv publish starts the branch's published sequence at .0, so the branch's first published revision is alice/shaders.0, and that publish is what makes the branch visible to anyone else.
That first publish also records what the branch descends from. FlexVault walks back through the drafts the branch was built on until it reaches a published revision, and records that one. If it finds none, fxv publish refuses the branch: the published store only appends, so a branch that starts without a root never gains one. The CLI does not report the recorded origin yet.
---
title: "A branch started from `main.456` and published"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.455"
commit id: "main.456"
branch "alice/shaders"
checkout "alice/shaders"
commit id: "alice/shaders.-.1"
commit id: "alice/shaders.-.2"
commit id: "alice/shaders.0" tag: "First publish"
Where your workspace sits on each branch¶
A workspace keeps track of where it is on each branch it has worked on. Because drafts are local, that position is the most recent draft revision this workspace created on that branch, and no other workspace can see it or arrive at it.
Say you have snapshots up to alice/shaders.0.4, and you switch to main to check something. fxv branch switch back to alice/shaders returns you to alice/shaders.0.4, rather than to the branch's published head, so your work in progress is where you left it. On a branch you have never worked on, there is no draft to return to and the switch takes you to that branch's latest published revision instead.
A colleague running the same fxv branch switch alice/shaders lands on their own draft of that branch, which is a different revision from yours. This is why a branch name alone does not identify a revision, and why only a published revision ID means the same thing in every workspace.
fxv sync operates within the branch the workspace is on. It brings in that branch's newer published revisions and merges your drafts on top of them, exactly as described above, and does not reach across branches.
Ownership and retirement¶
The owner prefix on a user branch records who created the branch. It does not restrict who may use it: once published, anyone connected to the repository can switch onto that branch, sync it, and publish to it.
Retiring a branch with fxv branch retire records that it is finished. A retired branch is left out of default listings and cannot be published to until it is restored, and nothing else about it changes. Its revisions stay readable, a workspace can still sit on it, and new branches can still be started from it. Nothing is deleted, and no command deletes a branch by design - this ensures the integrity and continuity of the revision history.
Both are bookkeeping rather than access control. FlexVault has no authentication yet, so ownership and retirement record intent and do not enforce it. Anyone can retire or restore any branch, including main, which is why retiring a global branch asks for --force.
Moving to an earlier revision¶
You can move your workspace to any earlier revision, published or draft, with fxv goto. This is non-destructive: your current state is snapshotted first, so nothing is lost and you can always move back.
Say you are working at main.459.7 and want to return to main.459.3. Running fxv goto main.459.3 updates your workspace to the files as they were at that draft revision, while main.459.4 through main.459.7 remain in your history.
---
title: "Workspace moved back to `main.459.3` from `main.459.7`"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.458"
commit id: "main.459"
branch main.459-drafts
commit id: "main.459.0"
commit id: "main.459.1"
commit id: "main.459.2"
commit id: "main.459.3" tag: "Head"
commit id: "main.459.4"
commit id: "main.459.5"
commit id: "main.459.6"
commit id: "main.459.7"
You can return to main.459.7 at any time by running fxv goto main.459.7. If instead you carry on working from main.459.3, new draft revisions branch off from that point, and your earlier drafts remain available in the history.
---
title: "Work continuing from `main.459.3`"
---
%%{init: {"themeVariables": {"git0":"#3f72bd","git1":"#2f8a72","git2":"#8b5fae","git3":"#b07d2e","gitBranchLabel0":"#ffffff","gitBranchLabel1":"#ffffff","gitBranchLabel2":"#ffffff","gitBranchLabel3":"#ffffff","commitLabelColor":"#111827","commitLabelBackground":"#f2f4f7","tagLabelColor":"#12305c","tagLabelBackground":"#dbe7f8","tagLabelBorder":"#8fb0da","lineColor":"#8b949e"}}}%%
gitGraph
commit id: "main.458"
commit id: "main.459"
branch main.459-drafts
commit id: "main.459.0"
commit id: "main.459.1"
commit id: "main.459.2"
commit id: "main.459.3"
branch main.459-drafts-2
checkout main.459-drafts-2
checkout main.459-drafts
commit id: "main.459.4"
commit id: "main.459.5"
commit id: "main.459.6"
commit id: "main.459.7"
checkout main.459-drafts-2
commit id: "main.459.8"
commit id: "main.459.9"
commit id: "main.459.10" tag: "Head"