| Crates.io | reconcile-text |
| lib.rs | reconcile-text |
| version | 0.8.0 |
| created_at | 2025-07-09 22:29:39.722553+00 |
| updated_at | 2025-12-06 22:04:05.858774+00 |
| description | Intelligent 3-way text merging with automated conflict resolution |
| homepage | https://schmelczer.dev/reconcile |
| repository | https://github.com/schmelczer/reconcile |
| max_upload_size | |
| id | 1745590 |
| size | 1,881,574 |
reconcile-text: conflict-free 3-way text mergingA Rust and TypeScript library for merging conflicting text edits without manual intervention. Unlike traditional 3-way merge tools that produce conflict markers, reconcile-text automatically resolves conflicts by applying both sets of changes (while updating cursor positions) using an algorithm inspired by Operational Transformation.
✨ Try the interactive demo to see it in action!
cargo add reconcile-text (reconcile-text on crates.io)npm install reconcile-text (reconcile-text on NPM)<<<<<<< markersInstall via crates.io:
cargo add reconcile-text
Alternatively, add reconcile-text to your Cargo.toml:
[dependencies]
reconcile-text = "0.5"
Then start merging:
use reconcile_text::{reconcile, BuiltinTokenizer};
// Start with the original text
let parent = "Hello world";
// Two users edit simultaneously
let left = "Hello beautiful world"; // Added "beautiful"
let right = "Hi world"; // Changed "Hello" to "Hi"
// Reconcile combines both changes
let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word);
assert_eq!(result.apply().text(), "Hi beautiful world");
See the merge-file example for another example or the library's documentation.
Install via NPM:
npm install reconcile-text
Then use it in your application:
import { reconcile } from 'reconcile-text';
// Start with the original text
const parent = 'Hello world';
// Two users edit simultaneously
const left = 'Hello beautiful world';
const right = 'Hi world';
const result = reconcile(parent, left, right);
console.log(result.text); // "Hi beautiful world"
See the example website source for a more complex example or the advanced examples document.
Collaborative editing presents the challenge of merging conflicting changes when multiple users edit documents simultaneously or asynchronously whilst offline. Traditional solutions like Conflict-free Replicated Data Types (CRDTs) or Operational Transformation (OT) work well when you control the complete editing infrastructure and can capture every individual operation (1). However, many workflows involve users editing with various tools, for example, Obsidian users editing Markdown files with various editors ranging from Vim to VS Code.
This creates Differential Synchronisation scenarios (2, 3): we only know the final state of each document, not the sequence of operations that produced it. This is the same challenge Git addresses, but Git requires manual conflict resolution. The key insight is that while incorrect merges in source code can introduce bugs, human text is more forgiving: a slightly imperfect sentence is often preferable to conflict markers interrupting the flow.
Note: Some text domains require more careful handling. Legal contracts, for instance, could have unintended meaning changes from conflicting edits that create double negations. At the same time, semantic conflicts can still arise when merging code, even in the absence of syntactic conflicts.
Differential sync is implemented by universal-sync and my Obsidian plugin vault-link, and it requires a merging tool which creates conflict-free results for the best user experience.
reconcile-text starts off similarly to diff3 (4, 5) but adds automated conflict resolution. Given a parent document and two modified versions (left and right), the following happens:
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 as excellent examples.
However, when only the end result of concurrent changes is observable, merge quality depends entirely on the quality of the underlying 2-way diffs. For instance, move operations cannot be supported because Myers' algorithm decomposes them into separate insert and delete operations, regardless of the merging algorithm used.
Contributions are welcome!
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
nvm install 22 && nvm use 22
nvm alias default 22
Install rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
scripts/test.shscripts/lint.shscripts/dev-website.shscripts/build-website.shscripts/bump-version.sh patch