Change Sets
Change sets are fundamental building blocks in Lix that represent collections of related changes. They form the nodes in the change graph and provide a structured way to manage modifications to your data.
What are Change Sets?
A change set in Lix is:
- A collection of related changes grouped together
- A node in the directed acyclic graph (DAG) of changes
- A logical unit that can be applied, reverted, or merged
- Similar to commits in Git, but with more granular change tracking
Change sets capture the state of your data at a specific point in time and track how that state evolves.
Change Set Structure
Each change set consists of:
- A unique identifier
- Optional connections to parent change sets
- A collection of changes (called elements)
- Optional labels for categorization
- Creation timestamp
In the database schema, this is represented by the change_set
table, with connections to other change sets in the change_set_edge
table, and its associated changes in the change_set_element
table.
Creating Change Sets
You can create change sets using the createChangeSet()
function:
import { createChangeSet } from "@lix-js/sdk";
// Create a change set
const changeSet = await createChangeSet({
lix,
// Optionally specify parent change sets
parents: [{ id: parentChangeSetId }],
// Optionally include specific changes
elements: [{ id: changeId1 }, { id: changeId2 }],
// Optionally add labels
labels: [{ key: "type", value: "feature" }]
});
console.log("Created change set:", changeSet.id);
Types of Change Sets
Lix supports different types of change sets for various purposes:
Basic Change Sets
Regular change sets created through createChangeSet()
function.
Checkpoints
Special change sets that mark significant points in your change history. Created using the createCheckpoint()
function:
import { createCheckpoint } from "@lix-js/sdk";
const checkpoint = await createCheckpoint({
lix,
name: "Version 1.0 Release",
description: "Stable release with all core features"
});
Checkpoints automatically add a label with key: "checkpoint"
to make them easy to identify.
Merge Change Sets
Change sets that combine changes from multiple parent change sets. Created using the createMergeChangeSet()
function:
import { createMergeChangeSet } from "@lix-js/sdk";
const mergeChangeSet = await createMergeChangeSet({
lix,
sources: [
{ id: branchAChangeSetId },
{ id: branchBChangeSetId }
]
});
Merge change sets handle the combination of changes from different branches, potentially resolving conflicts between them.
Working with Change Sets
Applying Change Sets
You can apply a change set to update the current state using the applyChangeSet()
function:
import { applyChangeSet } from "@lix-js/sdk";
await applyChangeSet({
lix,
changeSet
});
This updates the current state to reflect the changes in the change set.
Querying Change Sets
You can query change sets directly through the database interface:
// Get all change sets
const changeSets = await lix.db
.selectFrom("change_set")
.selectAll()
.execute();
// Get change sets with specific labels
const featureChangeSets = await lix.db
.selectFrom("change_set")
.innerJoin("change_set_label", "change_set.id", "change_set_label.entity_id")
.where("change_set_label.key", "=", "type")
.where("change_set_label.value", "=", "feature")
.selectAll("change_set")
.execute();
Analyzing Change Set Relationships
You can explore the relationships between change sets:
// Get parent-child relationships
const edges = await lix.db
.selectFrom("change_set_edge")
.selectAll()
.execute();
// Get the changes in a specific change set
const changes = await lix.db
.selectFrom("change")
.innerJoin("change_set_element", "change.id", "change_set_element.change_id")
.where("change_set_element.change_set_id", "=", changeSetId)
.selectAll("change")
.execute();
Change Sets and the Change Graph
Change sets form the nodes in Lix's change graph:
A → B → C → D (main branch)
↘
E → F (feature branch)
Each letter represents a change set, and arrows represent parent-child relationships. This graph structure enables powerful workflows:
- Branching: Create independent lines of development from a common ancestor
- Merging: Combine changes from different branches
- History Tracking: Understand how changes evolved over time
Example: Document Revision Workflow
Here's an example of using change sets to track document revisions:
// Initial document version
await handleFileInsert({
lix,
file: {
path: "/document.md",
data: new TextEncoder().encode("# Initial Draft")
}
});
// Create a change set for the initial version
const initialChangeSet = await createChangeSet({ lix });
// Mark it as a checkpoint
const checkpoint1 = await createCheckpoint({
lix,
name: "Initial Draft",
description: "First version of the document"
});
// Update the document
await handleFileUpdate({
lix,
file: {
path: "/document.md",
data: new TextEncoder().encode("# Revised Draft\n\nAdded content.")
}
});
// Create another change set for the revision
const revisionChangeSet = await createChangeSet({ lix });
// Mark it as another checkpoint
const checkpoint2 = await createCheckpoint({
lix,
name: "Revision 1",
description: "First revision with additional content"
});
// Query the change history
const history = await lix.db
.selectFrom("change_set")
.leftJoin("change_set_label", "change_set.id", "change_set_label.change_set_id")
.where("change_set_label.key", "=", "checkpoint")
.select([
"change_set.id",
"change_set.created_at",
"change_set_label.value as name"
])
.orderBy("change_set.created_at", "asc")
.execute();
Next Steps
Now that you understand change sets, explore these related concepts:
- Change Graph - How change sets connect to form a graph
- Versions - Named references to specific change sets
- Merging - How to combine changes from different branches
- Labels - How to categorize and organize change sets