Skip to content

Row Diff

Row-level diff is available through repository diffs for explaining what changed inside modified SQLite tables. The same machinery is still diagnostic for merge analysis and storage-level debugging.

Get storage LSNs:

Terminal window
graft sql "PRAGMA graft_debug_log_lsn;"

Run row-level diff:

Terminal window
graft sql "PRAGMA graft_debug_volume_diff = '2,4,rows';"

Run JSON row diff:

Terminal window
graft sql "PRAGMA graft_debug_volume_json_diff = '2,4,rows';"

Run repository row diff:

Terminal window
graft diff --rows HEAD~1 HEAD app.db
graft diff --json --rows HEAD~1 HEAD app.db

Human output groups changes by table:

Table: users
Changes: 2
+1 inserts
~1 updates
[1] UPDATE rowid=1
old: "Null, Text(\"Alice\")"
new: "Null, Text(\"Alicia\")"
[2] INSERT rowid=2
values: "Null, Text(\"Bob\")"

JSON output is easier to consume programmatically:

{
"from_lsn": 2,
"to_lsn": 4,
"tables": [
{
"name": "users",
"columns": ["id", "name"],
"changes": [
{ "op": "insert", "rowid": 2, "values": [null, "Bob"] },
{
"op": "update",
"rowid": 1,
"values": [null, "Alicia"],
"old_values": [null, "Alice"]
}
]
}
]
}

The row diff implementation operates on storage snapshots. Storage snapshots are addressed by LSN coordinates, while repository commands are addressed by commit ids and revspecs.

graft diff --rows resolves repository commits, index entries, or worktree state into file snapshots, then calls the row diff engine for modified database files. Added and deleted database files are still reported as file-level changes because row diff needs both a before and after snapshot.

The same row-analysis machinery can help explain and resolve merge conflicts. When Graft can inspect base, ours, and theirs snapshots, it reports table/rowid-level conflict information.

For the currently opened SQLite database, the PRAGMA merge path also uses this analysis to auto-merge non-conflicting row changes: Graft applies the theirs row diff to ours in a temporary SQLite database, imports the resulting snapshot, and stages it as the resolved merge result. Same-row conflicts, add/delete paths, schema changes that fail SQL application, and opaque virtual/shadow table changes stay in conflict stages.