Commits and Branches
Publish changes to several resources at once, work on branches, and pin the exact Project state a job read.
A commit is a snapshot of a whole Project. For every name, such as
frames, it records which version that name points to. Commits store only
those pointers, not the data, so they are cheap to make and you can keep
them forever. If you know Git, the model will feel familiar: branches move as
you commit, tags do not, and a job that records a commit ID can read the same
data again a year later. One difference from Git is that Spiral never merges
a conflicting commit for you.
An example
Your application writes new frames to frames and, in the same commit,
creates a Table of labels for them. A reader of main sees both changes or
neither, never the frames without their labels. A week later, a training job
that saved the address frames@c<commit-id> still reads the frames as they
were at that commit, even though main has moved on.
Commits
Each commit is immutable and has a commit ID. Every commit after the first has a parent, and a merge commit has two. The Catalog, the service that keeps names and commits, stores the commits of each Project.
Because one commit can change several resources, it is the unit of atomic change in Spiral. For example, one commit can append rows to one Table and create another. A reader sees all of the commit or none of it.
Branches and tags
A branch is a name that points to one commit, its head. A new Project has
the branch main. Committing on a branch moves it to the new commit.
Create a branch to make changes that readers of main do not see yet, then
merge it. A merge compares both branches with their common starting point
and reports any conflicts instead of publishing a partial result. You can also revert a
commit, which publishes a new commit that undoes its changes.
A tag is a name for one commit that never moves. Use a tag to name a release or the data behind a model.
When two writers race
To write, a client pins the head of a branch, writes new versions, and then asks the Catalog to commit them. The commit states what it expects to find; in the Rust SDK, that is the head it pinned. If another writer changed the branch in the meantime, the Catalog returns a conflict and publishes nothing, so a change you never saw is never overwritten.
For example, two jobs pin main at the same commit, and both write a new
version of frames. The first one commits. The second job's commit then fails with a
conflict. The new versions it wrote were based on data that is now out of
date, so it must start again: pin main again, read the Tables it depends
on, write its changes again, and commit.
A conflict is different from a lost response. If a commit times out and you do not know whether it happened, nothing has been decided against you: ask the Catalog for the outcome and, if the commit did not happen, send the same commit again. The data is already written and does not need to be written again. See Retries.
Pinning an exact state
When a job must read the same data every time, pin it to a commit or a version:
- Read through a commit address, such as
frames@c<commit-id>. - Save the version address
frames@v<version-id>with your results.
A later commit to the branch never changes what these addresses read.
Related pages
- Catalog documents branches, tags, history, merges and reverts.
- Commits shows how the Rust SDK publishes a commit, handles a moved branch and recovers a lost response.