# Text Diff

> Compare two texts by line, word or character and get a unified diff.

Category: Text & Data · Page: https://util.caruana.me/tools/diff/

## API, MCP and A2A

### Compare two texts (`text_diff`)

`GET|POST https://util.caruana.me/api/diff` · MCP tool `text_diff` · A2A skill `text_diff` · [reference](https://util.caruana.me/reference/text_diff/)

Compares `before` with `after` using jsdiff (Myers' O(ND) algorithm). `changes` is the full sequence of added, removed and unchanged pieces at the chosen granularity; concatenating the unchanged and removed values gives `before`, unchanged and added gives `after` (when ignoring whitespace or case, unchanged pieces are taken from `after`). `unified` and `hunks` are always a line-level unified diff (GNU `diff -u` format) with `context` lines, whatever the mode. `identical` is true when no differences remain under the chosen options; `exact_match` compares the raw strings. Limits: 200,000 characters per text; at most 5000 differing lines, or 2000 differing words or characters.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `before` | string (≤ 200000 chars) | yes | The original text. |
| `after` | string (≤ 200000 chars) | yes | The changed text. |
| `mode` | lines \| words \| characters | no | Granularity of `changes` and `stats`: `lines`, `words` (runs of letters/digits/underscore; each punctuation mark is its own token), or `characters` (Unicode code points). |
| `ignore_whitespace` | boolean | no | `lines`: compare lines with leading and trailing whitespace trimmed. `words`: ignore differences in the whitespace between words. Not supported in `characters` mode (422). |
| `ignore_case` | boolean | no | Compare case-insensitively (JavaScript `toLowerCase`), in every mode and in the unified diff. |
| `context` | integer (0–1000) | no | Unchanged lines shown around each change in `unified` and `hunks` (GNU diff default: 3). |
| `from_label` | string (≤ 200 chars) | no | Label for the `---` header line of the unified diff, e.g. a file name `a/src/app.ts`. |
| `to_label` | string (≤ 200 chars) | no | Label for the `+++` header line of the unified diff. |

Privacy: The server receives both texts in full, compares them in memory, and returns the differences. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. For confidential text use the page, which compares in your browser and sends nothing.


## User guide

The tool sits in a window that works like a desktop app: menus on the top row and the **full-screen** icon at its right (**Esc** leaves full screen). The set-up options described below are in **Settings → Options…**, an overlay, and the **Settings** menu also lists the common choices directly. To keep the options in view instead, choose **View → Inline config**; the page remembers that choice. **Help → Keyboard shortcuts…** lists the keys.

1. Paste the original into **Before** and the changed version into **After**.
2. Choose how to compare:
   - **Lines** for code, configuration and logs;
   - **Words** for prose;
   - **Characters** for short strings, identifiers or a single changed line.
3. Optionally tick **Ignore whitespace** or **Ignore case**.
4. Read the result:
   - the summary line counts what was added and removed;
   - **Changes** shows both texts merged, with removed text struck through in red and added text underlined in green;
   - **Unified diff** is the standard patch format. Copy it or download it as `changes.diff`.

To apply the unified diff to a file, set both labels to the file's path (e.g. `src/app.ts`) and run `patch -p0 < changes.diff`. With `a/src/app.ts` and `b/src/app.ts` labels, use `git apply changes.diff` or `patch -p1`.

**Swap** exchanges the two texts, which reverses the diff.

## Specification

**Algorithm.** jsdiff's implementation of Myers' O(ND) difference algorithm, which finds a shortest edit script. When several shortest scripts exist, jsdiff's choice is used.

**Modes.**

| Mode         | Token                                                                                                                                                   | Counts in `stats` |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| `lines`      | a line including its line break; `\n` and `\r\n` both end a line                                                                                        | lines             |
| `words`      | a run of ASCII letters, digits, `_` and Latin-1/Latin Extended letters; every other non-space character (punctuation, CJK, emoji) is a token of its own | word tokens       |
| `characters` | a Unicode code point (an emoji is one character)                                                                                                        | code points       |

**Options.**

| Option              | `lines`                                                                                                                                           | `words`                                                   | `characters`                                              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- |
| `ignore_whitespace` | Lines compare equal if they match after trimming leading and trailing whitespace (so `a\r\n` equals `a\n`). Whitespace inside a line still counts | Whitespace between words is ignored (`a  b` equals `a b`) | Not supported: 422 error. On the page the box is disabled |
| `ignore_case`       | Case-insensitive (`toLowerCase`)                                                                                                                  | Same                                                      | Same                                                      |

Without `ignore_whitespace`, whitespace is significant in every mode: in `words` mode, `a  b` and `a b` differ.

**Structured output.**

- **`changes`:** every piece of both texts, in order, typed `added`, `removed` or `unchanged`, with its size (`count`) in the mode's unit. A replacement is a `removed` piece followed by an `added` piece.
- **Rebuilding the texts:** concatenating `unchanged` + `removed` values reproduces `before`; `unchanged` + `added` reproduces `after`. When an ignore option is set, unchanged pieces carry the `after` text.
- **`identical`:** true when `changes` contains nothing but `unchanged`, i.e. equal under the options.
- **`exact_match`:** compares the raw strings.

**Statistics.**

- **`added`, `removed`, `unchanged`:** totals in the mode's unit.
- **`change_blocks`:** number of contiguous runs of changed pieces.
- **`lines_added`, `lines_removed`:** `+` and `-` lines of the unified diff, as `diffstat` counts them.
- **`chars_added`, `chars_removed`:** code points in the added and removed pieces.

**Unified diff.**

- **Granularity:** always line-based, whatever the mode, using the same `ignore_whitespace` and `ignore_case`. Context lines shown when ignoring come from `after`.
- **Headers:** `--- <from_label>` and `+++ <to_label>`, then hunks `@@ -<start>,<count> +<start>,<count> @@` with `context` unchanged lines around each change (default 3, as GNU diff). Changes separated by at most twice `context` unchanged lines (6 by default) share one hunk.
- **Counts in headers:** jsdiff always writes the count, even when it is 1 (`@@ -11,1 +11,1 @@`; GNU diff writes `@@ -11 +11 @@`). `patch` and `git apply` accept both.
- **Empty side:** an empty range is written as start 0, count 0 (`@@ -0,0 +1,1 @@`).
- **Missing final newline:** a line without one is followed by `\ No newline at end of file`.
- **Labels:** a label containing non-ASCII characters, control characters, `"` or `\` is quoted C-style, as Git does (`"caf\303\251.txt"`).
- **No differences:** `unified` is an empty string and `hunks` is empty.

`hunks` holds the same data as `unified`, with the header parsed into numbers and each line kept with its ` `, `-` or `+` prefix (line breaks removed).

**Limits.**

- **Text size:** each text may be up to 200,000 characters (UTF-16 code units).
- **Edit size:** a comparison stops with a 422 error once the edit script would exceed 5,000 lines (`lines`, and the unified diff in every mode) or 2,000 tokens (`words`, `characters`). Myers' algorithm costs time proportional to text length × number of edits, so these caps bound the worst case. For heavily rewritten texts use `lines` mode or compare smaller sections.
