Skip to main content

reconcile-text: conflict-free 3-way text merging

reconcile-text merges two edited versions of a document using their common parent, without leaving conflict markers. It combines changes from both versions and adjusts any cursor positions you pass in. You can use it from Rust, JavaScript/TypeScript, or Python.

It's designed for syncing text files when all you have are snapshots: the original document and two edited copies. You don't need to record editing operations or keep extra document metadata between merges.

Try it

✨ Try the interactive demo to see it in action!

For the motivation and algorithm behind the library, read Conflict-Free Three-Way Text Merging.

Quick start

In each example, one edit adds "beautiful" and the other changes "Hello" to "Hi". The merge keeps both changes.

Rust

Install from crates.io:

cargo add reconcile-text
use reconcile_text::{reconcile, BuiltinTokenizer};

let parent = "Hello world";
let left = "Hello beautiful world";  // Added "beautiful"
let right = "Hi world";              // Changed "Hello" to "Hi"

let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word);
assert_eq!(result.apply().text(), "Hi beautiful world");

The merge-file example shows how to merge files from the command line. See the Rust API documentation for the full API.

JavaScript/TypeScript

Install from npm:

npm install reconcile-text
import { reconcile } from 'reconcile-text';

const parent = 'Hello world';
const left = 'Hello beautiful world';
const right = 'Hi world';

const result = reconcile(parent, left, right);
console.log(result.text); // "Hi beautiful world"

The TypeScript guide shows how to track cursors, see where changes came from, and work with compact diffs. For an example of using the library in an editor, see the demo website source.

The Node.js and browser builds use the Rust library compiled to WebAssembly. React Native uses a JavaScript build generated by Binaryen's wasm2js. It's available through the package's react-native entry point and runs in Hermes without a WebAssembly global.

Python

Install from PyPI with uv or pip:

uv add reconcile-text
# or: pip install reconcile-text
from reconcile_text import reconcile

parent = "Hello world"
left = "Hello beautiful world"
right = "Hi world"

result = reconcile(parent, left, right)
print(result["text"])  # "Hi beautiful world"

The Python package uses native bindings to the same Rust library. For merging files from the command line, see the merge-file example. The Python guide shows how to track cursors, see where changes came from, and work with compact diffs.

Why merge snapshots?

Suppose you edit a note in Obsidian on one device and Vim on another while they're offline. When the devices reconnect, your file sync service has two edited copies to combine. It can read the files, but it can't ask either editor which operations produced them.

Keeping the original version gives it a common parent to compare both copies against. Git works with the same inputs and leaves conflicts it can't resolve for a person to handle. That's a useful default for source code. For notes and prose, an automatic merge can be more useful, even if the wording occasionally needs tidying up.

How it works

Like diff3, reconcile-text starts with a parent document and two edited versions, left and right. It then:

  1. Splits the text into tokens, such as words, characters, or lines.
  2. Finds each side's changes using Myers' diff algorithm to compare the parent with left and with right.
  3. Cleans up the diffs by reordering and grouping operations to keep related changes together.
  4. Combines the edits using operational transformation (OT) principles to account for changes from both sides and update cursor positions.

Whilst the primary goal of reconcile-text isn't to implement OT, it provides an elegant way to merge Myers' diff outputs. (For a dedicated Rust OT implementation, see operational-transform-rs.) The same could be achieved with CRDTs, which many libraries implement well for text (see Loro, cola, and automerge).

Choosing a tokeniser

The tokeniser decides which pieces of text stay together during a merge:

  • Word keeps words together. This is the default in JavaScript/TypeScript and Python.
  • Character works with individual Unicode code points, so edits can be combined within a word.
  • Line treats each line as a unit.
  • Markdown builds on word tokenisation. It preserves newlines and keeps block prefixes, such as list markers, attached to the first word.

In Rust, you choose a tokeniser explicitly and can supply your own function. The merge keeps tokens intact, though the Markdown tokeniser doesn't check whether the merged document is valid Markdown. Try the options in the demo to see how they handle your text.

Comparison with other approaches

diff3 and Git

diff3 and Git's default text merge leave conflict markers where they can't combine changes automatically. Git also has a union merge driver that keeps lines from both sides of a conflict. Because it works with whole lines, edits to different words in the same sentence can leave you with two versions of that sentence. With word tokenisation, reconcile-text can combine those edits within the line.

diff-match-patch

diff-match-patch compares two texts and applies patches using fuzzy matching. This is useful when the text around a change may have shifted. Patches can fail, so you need to check the success flags returned when applying them.

reconcile-text compares each edited version with the common parent before combining the changes. The comparison example shows how both approaches handle overlapping replacements and a sentence inserted into a rewritten paragraph.

CRDTs and OT

Libraries such as Yjs, Automerge, Loro, and cola use conflict-free replicated data types (CRDTs) to merge updates across replicas. They keep document structures and metadata between edits, so your application needs to store and work with that state. You can also turn differences between snapshots into CRDT updates; using a CRDT doesn't require recording every keystroke.

With OT, applications exchange editing operations and use a protocol to coordinate them. Libraries such as ot.js and Rust's operational-transform-rs provide tools for transforming those operations. reconcile-text uses OT ideas internally, deriving its operations from the snapshots when you call it.

The main difference is what your application keeps between merges. reconcile-text takes a parent and two edited copies, and merges those two copies in one call. A collaborative editing system built around CRDTs or OT also manages the state and coordination needed to keep replicas in sync over time.

Development

Contributions are welcome!

Python setup

Install uv, then build the extension for Python-only development:

cp README.md LICENSE reconcile-python/
cd reconcile-python
uv run maturin develop

Node.js setup

  1. Install nvm:
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    
  2. Install and use Node 22:
    nvm install 22 && nvm use 22
    
  3. Optionally, make Node 22 your default:
    nvm alias default 22
    

Rust toolchain

Install rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Scripts

After installing Node 22, rustup, and uv, run scripts/check.sh for the full install, build, lint, test, and demo build sequence.

For individual steps, run scripts/setup.sh once to install dependencies and scripts/build.sh to build the JS and Python bindings. Run the build again after changing Rust or binding code. The lint, test, and website scripts use those existing dependencies and build outputs.

  • Install dependencies: scripts/setup.sh
  • Build bindings: scripts/build.sh (or scripts/build-js.sh for JS only)
  • Run tests: scripts/test.sh
  • Check lint and formatting: scripts/lint.sh
  • Run the demo locally: scripts/dev-website.sh
  • Build the demo: scripts/build-website.sh
  • Publish a new version: scripts/bump-version.sh patch

License

MIT

Metadata

Release files for reconcile-text 0.14.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for reconcile-text 0.14.1
File Size Uploaded Provenance Actions
reconcile_text-0.14.1.tar.gz 772.3 kB Without provenance Details for reconcile_text-0.14.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for reconcile-text 0.14.1
File Actions
reconcile_text-0.14.1-pp311-pypy311_pp73-win_amd64.whl PyPy 3.11 PyPy 3.11 7.3 Windows x86-64 Details for reconcile_text-0.14.1-pp311-pypy311_pp73-win_amd64.whl
reconcile_text-0.14.1-cp314-cp314t-win_amd64.whl CPython 3.14 CPython 3.14 free-threading Windows x86-64 Details for reconcile_text-0.14.1-cp314-cp314t-win_amd64.whl
reconcile_text-0.14.1-cp313-cp313t-win_amd64.whl CPython 3.13 CPython 3.13 free-threading Windows x86-64 Details for reconcile_text-0.14.1-cp313-cp313t-win_amd64.whl
reconcile_text-0.14.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details for reconcile_text-0.14.1-cp39-abi3-win_amd64.whl
reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details for reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl
reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl CPython 3.9 abi3 Linux musl 1.2+ ARM64 Details for reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl
reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details for reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details for reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
reconcile_text-0.14.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details for reconcile_text-0.14.1-cp39-abi3-macosx_11_0_arm64.whl
reconcile_text-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details for reconcile_text-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl

Total release size: 4.0 MB

Release files / reconcile_text-0.14.1.tar.gz

Download URL reconcile_text-0.14.1.tar.gz
Size 772.3 kB
Tags Source
SHA-256 checksum
How to use checksums
08572158bfd7a73f04bcf1b8457f75628839c6dddc9c68a34d6da3df352e2645
BLAKE2b-256 checksum
How to use checksums
c2291e48a28e9ca3c2309e8ee6ab0d3ac94b3907fb57ecbcd9b592c588b072e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-pp311-pypy311_pp73-win_amd64.whl

Download URL reconcile_text-0.14.1-pp311-pypy311_pp73-win_amd64.whl
Size 151.9 kB
Tags PyPy 3.11 PyPy 3.11 7.3 Windows x86-64
SHA-256 checksum
How to use checksums
0b9d3fab1aec38d7367678375454c881a31f7974caa1e7e69e9db3945b7a610b
BLAKE2b-256 checksum
How to use checksums
f0d09403445889dbbbde0cd06c2e0689e42265c700d42bb74813c8916d67cff8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp314-cp314t-win_amd64.whl

Download URL reconcile_text-0.14.1-cp314-cp314t-win_amd64.whl
Size 149.1 kB
Tags CPython 3.14 CPython 3.14 free-threading Windows x86-64
SHA-256 checksum
How to use checksums
8d9f6298820aca0197d6aabacd0562181a07ad8e796f86a0a358cbe5ae53716d
BLAKE2b-256 checksum
How to use checksums
3602c9163c9320acd8cd73c8a4e7a32c3b9870baa5e020a91169ce246734bca2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp313-cp313t-win_amd64.whl

Download URL reconcile_text-0.14.1-cp313-cp313t-win_amd64.whl
Size 149.1 kB
Tags CPython 3.13 CPython 3.13 free-threading Windows x86-64
SHA-256 checksum
How to use checksums
7481f7e6f04e76afa33fd365905b5796cbe76521f896960e23bebe8b3b21bc87
BLAKE2b-256 checksum
How to use checksums
3fb43227101ac0ec3a399ba7e1f9f80840cf93a0f884ba009e05bb2a3f56804d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-win_amd64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-win_amd64.whl
Size 153.3 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5b1b71beea29f3e28960f7c69aff4d84bdac98b0e6b925a0e0e7da2d62bbb665
BLAKE2b-256 checksum
How to use checksums
3d2acfa443770146350de886c3bf4644ebb404ce1d21f5adaffba4d88d5e8b0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl
Size 268.7 kB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
0deb7120e910365125753caed9d92a38c2aad018a962ec9dcef7b344decd4d97
BLAKE2b-256 checksum
How to use checksums
56ffecc26dec41e3275cc3c68ade60e523d39858ca8c14336a60a872dd8bef00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl
Size 257.8 kB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
13ba7162af1fd7fe4d2ec8ed5e1c276a415f36a520f475fa748c7a8867b0626b
BLAKE2b-256 checksum
How to use checksums
7192161b1d77b23fac3dc6b432427ab4720fc6b0718cbfbf16d8d55d9ac12696
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 270.1 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
05fda7209e8f476388daf4a95bc59dba302d049a6f80edb5c929bb5dd6f052f1
BLAKE2b-256 checksum
How to use checksums
20cc3399c1f62843b27f90251487fdbf7638b764b3f72f7e60d16d786e3ac3cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 259.1 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
8417762ae1aaf9f158ba11fde98e1218221c97651b2832a89cc15fcc0469ddc3
BLAKE2b-256 checksum
How to use checksums
ac4d7244c021b48569e3f4b30cb308367d03e84ef7c4fe2ea7c380b31dc67eae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-macosx_11_0_arm64.whl
Size 761.4 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
44adb09f0066c6270fd5d77ef45b2d8d149f85ffc0b20668228c8c6203be7c5b
BLAKE2b-256 checksum
How to use checksums
659e963697e5ad2db79872b1e739ee01e187eacc0af19e7198b633ccb15dfd85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Release files / reconcile_text-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl

Download URL reconcile_text-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl
Size 774.7 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
a1b9748ba8c87738a24fe1feec1bb1ec3af9984ac0ac43d038040569d2caba4c
BLAKE2b-256 checksum
How to use checksums
5510a1f89dc8de1dd1a11145d7bcaeafc6aab43c6f21e98a424b6c7544ecde96
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.12.6

Security for reconcile-text 0.14.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page