logo
  • Docs
  • API Reference
    Introduction
    What is Lix?
    Getting Started
    Comparison to Git
    Lix for AI Agents
    Essentials
    How Lix Works
    Querying Changes
    Data Model
    Plugins
    Persistence
    Guides
    Versions (Branching)
    History
    Diffs
    Attribution (Blame)
    Change Proposals
    Validation Rules
    Undo/Redo
    Restore
    Conversations
    Labels
    Key-Value Store
    Environment API
    Testing
    React Integration
    Logging & Debugging
    Deterministic Mode
    Metadata
    Writer Key
    Architecture
    Lix as File Format
    Previous pageVersions (Branching)Next pageDiffs

    #History

    Query complete change history for files and entities using SQL.

    History

    #How History Works

    History walks the commit graph backwards from a starting point:

    Querying from Checkpoint 3 traverses backwards, showing states at C3, C2, and C1. The lixcol_depth field indicates distance from the starting point (0 = starting point, 1 = one step back, etc.).

    NOTE

    The commit graph is global and shared across all versions. You query at a specific commit, not a version. All versions reference the same unified graph structure.

    #File History

    Query file state at specific checkpoints to see how it changed over time:

    import { openLix, createCheckpoint } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    // Query file state at each checkpoint to show history
    const history = [];
    for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
      const state = await lix.db
        .selectFrom("file_history")
        .where("path", "=", "/config.json")
        .where("lixcol_root_commit_id", "=", checkpoint.id)
        .where("lixcol_depth", "=", 0)
        .select(["data"])
        .executeTakeFirstOrThrow();
    
      history.push({
        checkpoint: checkpoint.id,
        data: JSON.parse(new TextDecoder().decode(state.data))
      });
    }
    
    console.log("File history:", history);

    Query file_history with lixcol_root_commit_id = checkpoint.id and lixcol_depth = 0 to get the file state at that specific checkpoint.

    #Entity History

    Query history for specific entities like key-value pairs, custom data types, or plugin-defined entities:

    import { openLix, createCheckpoint } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    // Query file state at each checkpoint to show history
    const history = [];
    for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
      const state = await lix.db
        .selectFrom("file_history")
        .where("path", "=", "/config.json")
        .where("lixcol_root_commit_id", "=", checkpoint.id)
        .where("lixcol_depth", "=", 0)
        .select(["data"])
        .executeTakeFirstOrThrow();
    
      history.push({
        checkpoint: checkpoint.id,
        data: JSON.parse(new TextDecoder().decode(state.data))
      });
    }
    
    console.log("File history:", history);
    // Create a key-value pair to demonstrate entity-level history
    await lix.db
      .insertInto("key_value")
      .values({ key: "app_version", value: "1.0.0" })
      .execute();
    
    await createCheckpoint({ lix });
    
    await lix.db
      .updateTable("key_value")
      .where("key", "=", "app_version")
      .set({ value: "1.1.0" })
      .execute();
    
    const checkpoint4 = await createCheckpoint({ lix });
    
    // Query history for the key-value entity from checkpoint 4
    const entityHistory = await lix.db
      .selectFrom("state_history")
      .where("entity_id", "=", "app_version")
      .where("schema_key", "=", "lix_key_value")
      .where("root_commit_id", "=", checkpoint4.id)
      .orderBy("depth", "asc")
      .select(["snapshot_content", "depth"])
      .execute();
    
    console.log("Entity history for app_version:",
      entityHistory.map(h => ({
        value: h.snapshot_content.value,
        depth: h.depth
      }))
    );

    Entity-level history enables fine-grained audit trails and tracking for individual data points beyond files.

    #Query History at Checkpoints

    Query file state at any specific checkpoint:

    import { openLix, createCheckpoint } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    // Query file state at each checkpoint to show history
    const history = [];
    for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
      const state = await lix.db
        .selectFrom("file_history")
        .where("path", "=", "/config.json")
        .where("lixcol_root_commit_id", "=", checkpoint.id)
        .where("lixcol_depth", "=", 0)
        .select(["data"])
        .executeTakeFirstOrThrow();
    
      history.push({
        checkpoint: checkpoint.id,
        data: JSON.parse(new TextDecoder().decode(state.data))
      });
    }
    
    console.log("File history:", history);
    
    // Create a key-value pair to demonstrate entity-level history
    await lix.db
      .insertInto("key_value")
      .values({ key: "app_version", value: "1.0.0" })
      .execute();
    
    await createCheckpoint({ lix });
    
    await lix.db
      .updateTable("key_value")
      .where("key", "=", "app_version")
      .set({ value: "1.1.0" })
      .execute();
    
    const checkpoint4 = await createCheckpoint({ lix });
    
    // Query history for the key-value entity from checkpoint 4
    const entityHistory = await lix.db
      .selectFrom("state_history")
      .where("entity_id", "=", "app_version")
      .where("schema_key", "=", "lix_key_value")
      .where("root_commit_id", "=", checkpoint4.id)
      .orderBy("depth", "asc")
      .select(["snapshot_content", "depth"])
      .execute();
    
    console.log("Entity history for app_version:",
      entityHistory.map(h => ({
        value: h.snapshot_content.value,
        depth: h.depth
      }))
    );
    // Query file state at a specific checkpoint
    const stateAtCheckpoint2 = await lix.db
      .selectFrom("file_history")
      .where("path", "=", "/config.json")
      .where("lixcol_root_commit_id", "=", checkpoint2.id)
      .where("lixcol_depth", "=", 0)
      .select(["data"])
      .executeTakeFirstOrThrow();
    
    console.log("State at checkpoint 2:",
      JSON.parse(new TextDecoder().decode(stateAtCheckpoint2.data))
    );

    Use lixcol_depth = 0 to get the state at that exact checkpoint.

    TIP

    Lix automatically commits every change. Checkpoints are optional labels for important moments. Always filter by lixcol_root_commit_id to scope history to a specific point and avoid mixing timelines.