Rift Star on GitHubv0.3.0
a.sql, b.sql: a 64 MiB pair
$ rift --summary a.sql b.sql
a: 843633 lines, 67108879 bytes
b: 845034 lines, 67182379 bytes
equal    436 segments, 835632 lines in a, 835632 in b
differs  435 segments, 8001 lines in a, 9402 in b
first differing ranges (0-based, end exclusive):
  a 381..383  b 381..382
  a 1005..1011  b 1004..1010
moved    62 blocks, 2737 lines
  a 21557..21560  b 22434..22437
peak heap 78.6 MiB of 256.0 MiB (82397510 bytes)
time 0.13 s
$ 
Real output on a generated pair: rift-gen --seed 7 --size 64M --profile sql. The page shows 12 selected lines of the full summary. built from 3fcd321, Apple M1, 17 GB RAM, macOS 27.0.1.

What it does

Five ways to run Rift. Every output on this page ran on files made by rift-gen --seed 7, and each box says where it shows selected lines of a longer output.

Plain diff and --summary unified diff, then the counts

rift a b writes a unified diff that patch applies to the first file to get the second. --summary counts first, so you know how much differs before you write a diff.

the first hunk of 434
$ rift a.sql b.sql
--- a.sql+++ b.sql@@ -380,7 +380,6 @@ INSERT INTO public.orders (id, customer_id, status, amount, note) VA… 381	178722	cancelled	2930.54	wmojdqnzik INSERT INTO public.orders (id, customer_id, status, amount, note) VA…-383	668107	shipped	5325.74	zu INSERT INTO public.orders (id, customer_id, status, amount, note) VA… 385	359182	cancelled	475.15	ztbyhdlmxk INSERT INTO public.orders (id, customer_id, status, amount, note) VA…
One hunk of the full diff. Timestamps dropped, lines cut at 70 columns.
the counts of --summary
$ rift --summary a.sql b.sql | grep -E "^(a:|b:|equal|differs|peak heap|time)"
a: 843633 lines, 67108879 bytes
b: 845034 lines, 67182379 bytes
equal    436 segments, 835632 lines in a, 835632 in b
differs  435 segments, 8001 lines in a, 9402 in b
peak heap 78.6 MiB of 256.0 MiB (82397510 bytes)
time 0.17 s
Six of the summary's 31 lines.

Guide: plain diff, --summary.

Key mode rows compared by key, not by position

An export written in a different row order diffs by content, not by position. --key takes a CSV column, an NDJSON field or a pg_dump COPY column, and lists the keys that were added (+), removed (-) or changed (~), with the line of the record in each file.

rift --key csv:id
$ rift --key csv:id a.csv b.csv > keys.txt
$ for k in "~" "-" "+"; do grep -m 2 "^$k" keys.txt; done
$ cut -c1 keys.txt | sort | uniq -c
~6650	6419	6634
~6651	6420	6635
-144	145	-
-145	146	-
+191	-	140
+192	-	141
4024 -
6535 +
5047 ~
Two lines of each kind, then the number of keys of each kind in all. A 32 MiB CSV pair, same keys, shuffled rows.

Guide: key mode. Use case: compare CSV files by key.

Git rift as the diff driver

Git calls any file over 512 MiB binary and prints no diff. Name the large files in .gitattributes and git runs rift git-diff for them, with git's own headers. It ships in 0.3.0.

git diff on a 640 MiB dump
$ echo '*.sql diff=rift' >> .gitattributes
$ git config diff.rift.command 'rift git-diff --mem 1G'
$ git diff dump.sql > out.diff
$ head -n 10 out.diff | cut -c1-90
diff --git a/dump.sql b/dump.sqlindex 86d4681..0000000 100644--- a/dump.sql+++ b/dump.sql@@ -380,7 +380,6 @@ INSERT INTO public.orders (id, customer_id, status, amount, note) VALUES (380, 514366, 's 381	178722	cancelled	2930.54	wmojdqnzik INSERT INTO public.orders (id, customer_id, status, amount, note) VALUES (382, 679322, 'c-383	668107	shipped	5325.74	zu INSERT INTO public.orders (id, customer_id, status, amount, note) VALUES (384, 442363, 'n
A scratch repository with the dump committed and then changed. The first 10 lines of the output, cut at 90 columns.

Guide: git integration. Use case: git says "Binary files differ".

MCP a diff an MCP client can query

rift-mcp lets an MCP client, such as an editor or a chat app, ask about the diff of two huge files without loading them. It has three tools, summary, hunks_at and next_difference, and every answer has a size limit.

rift-mcp, one tools/call
$ rift-mcp
-> {"method":"tools/call","params":{"name":"summary","arguments":{"a":"a.sql","b":"b.sql"}}}
<- {
  "a_lines": 843633,
  "b_lines": 845034,
  "differs": {"a_lines":10936,"b_lines":12337,"segments":435},
  "equal": {"a_lines":832697,"b_lines":832697,"segments":435},
  "first_differences": [
    {"a":{"count":9,"start":378},"b":{"count":8,"start":378},"status":"differs"},
    {"a":{"count":21,"start":1006},"b":{"count":21,"start":1005},"status":"differs"},
    ...
  ],
  "mem_budget_bytes": 67108864,
  "more_differences": 415
}
Captured over standard input and output after the initialize handshake. The call is shown without its jsonrpc and id fields. The answer is cut: its text copy, the byte counts and all but two of the first 20 ranges are left out. rift-mcp builds its diff with a 64 MiB budget unless the call sets mem, so the counts differ from the CLI's.

Guide: MCP server.

VS Code the extension

Compare two huge files from VS Code and open any difference in VS Code's own diff editor.

The Rift view in VS Code listing the differences of a 1 GiB pair, then one difference open in the diff editor
A recording on a 1 GiB pair from rift-gen --seed 7, on an Apple M1. Also as an MP4.

Install from the Marketplace Install from Open VSX

Guide: the VS Code extension.

The file grows. The heap does not.

Input size, to scale up to 2 GiBPeak heap against the 256 MiB limit
  1. input, each of A and B256 MiBeach of A and B
    peak heap83.2 MiBRSS 112.8 MiB, full diff in 0.28 s
  2. input, each of A and B1 GiBeach of A and B
    peak heap83.6 MiBRSS 113.0 MiB, full diff in 0.69 s
  3. input, each of A and B2 GiBeach of A and B
    peak heap83.6 MiBRSS 113.4 MiB, full diff in 1.20 s

Rift at --mem 256M on generated SQL pairs, full diff, Intel Core i5-10600, 16 GB RAM, Ubuntu 26.04.1 LTS, files in tmpfs, median of three runs, 2026-11-06. The gauge is the 256 MiB limit; the bar is Rift's own peak heap count. Source: bench/results/linux-intel-core-i5-10600-2026-11-06/report.md. The largest pair measured so far is 10 GiB, on a Mac (see the table below).

What a demo looks like summary, then the terminal view

1 GiB pair, full diff Apple M1

Rift against diff on a 1 GiB pair Rift took 0.93 s and 130.8 MiB. System diff took 17.55 s and 3.2 GiB. Apple M1, 16 GB RAM, macOS, warm page cache. Wall time rift0.93 s diff17.55 s Peak memory rift130.8 MiB diff3.2 GiB

Apple M1, 16 GB RAM, internal SSD, macOS, warm page cache. Source: README.md at the v0.1.0 tag, section Benchmarks. All tables, with commands, are on the benchmarks page.

Apple M1 macOS, warm page cache

PairToolTimePeak memory
1 GiBrift --summary1.26 s127.7 MiB
1 GiBrift (full diff)0.93 s130.8 MiB
1 GiBsystem diff17.55 s3.2 GiB
10 GiBrift --summary13.37 s136.5 MiB
10 GiBrift (full diff)26.16 s136.5 MiB
10 GiBdiff, git diffdid not run: needs more than 16 GB of RAM

git diff has no 1 GiB row: it treats files over 512 MiB as binary and prints no diff. Peak memory is the resident set; Rift's heap peaked at 83.5 MiB on the 1 GiB pair and 94.1 MiB on the 10 GiB pair.

Intel Core i5-10600 Ubuntu 26.04.1 LTS, tmpfs

PairToolTimePeak memoryPeak heap
256 MiBrift (full diff)0.28 s112.8 MiB83.2 MiB
256 MiBGNU diff1.62 s776.3 MiB
256 MiBgit diff --no-index2.57 s1.0 GiB
1 GiBrift (full diff)0.69 s113.0 MiB83.6 MiB
1 GiBGNU diffkilled at the memory cap
1 GiBgit diff --no-index42.59 s5.3 MiB
2 GiBrift (full diff)1.20 s113.4 MiB83.6 MiB

Median of three runs. GNU diff was killed at a 3 GiB cap on the 1 GiB pair. On the 1 GiB pair git diff printed no diff: it treats the files as binary. More rows on the benchmarks page.

What is guaranteed

  • The output is a correct edit script: applied to the first file it gives exactly the second, byte for byte.
  • A line counts as unchanged only after its bytes are compared. Hashes only propose matches.
  • The heap stays at or under --mem. Files are read with pread into fixed buffers, never mmap.
  • A region Rift could not refine within the budget is marked approximate in every output.

What is not

  • The diff is not minimal.
  • A moved block shows in the unified diff as a deletion and an insertion. --summary, --stats and the terminal view list moved blocks, and detection does not find every block.
  • Windows is not supported.