Text Diff

Сравнивайте два текста рядом с построчной подсветкой различий

Попробовать пример

Текст A

Текст B

Работает локально · Безопасно вставлять секреты
Работает локально · Безопасно вставлять секреты

Что такое text diff?

Text diff (сокращение от «difference» — «различие») — это результат сравнения двух блоков текста с определением того, какие строки были добавлены, удалены или остались без изменений. Концепция восходит к утилите diff операционной системы Unix, впервые выпущенной в 1974 году в составе Version 5 Unix. Сегодня text diff является основой систем контроля версий, таких как Git, где каждый коммит хранит diff, а не полную копию каждого файла.

Алгоритм diff находит Наибольшую Общую Подпоследовательность (LCS) между двумя последовательностями строк. Строки, входящие в LCS, помечаются как неизменённые. Строки исходного текста, не вошедшие в LCS, помечаются как удалённые. Строки изменённого текста, не вошедшие в LCS, помечаются как добавленные. В результате получается минимальный набор изменений, необходимых для преобразования одного текста в другой.

Вывод diff бывает нескольких форматов. Unified diff (формат по умолчанию для git diff) предваряет удалённые строки знаком минус, а добавленные — знаком плюс. Side-by-side diff располагает оба текста в параллельных столбцах. Данный инструмент использует построчное сравнение с цветовым кодированием: зелёный — добавления, красный — удаления, нейтральный — неизменённые строки. Неизменённые строки по умолчанию отображаются без префикса, но их можно скрыть, чтобы сосредоточиться только на изменениях.

Зачем использовать онлайн-инструмент для сравнения текстов?

Сравнение текстов в терминале требует установки утилит и знания ключей командной строки. Браузерный инструмент diff полностью устраняет это неудобство.

Мгновенное сравнение
Вставьте два блока текста и сразу увидьте выделенные различия. Никаких файлов, никаких команд, никакого разбора вывода.
🔒
Обработка данных на стороне клиента
Всё сравнение происходит в вашем браузере с помощью JavaScript. Ваш текст не покидает устройство — это важно при сравнении конфигурационных файлов, учётных данных или проприетарного кода.
📋
Вывод, готовый к копированию
Результат diff использует стандартные префиксы + / -, соответствующие формату unified diff. Вы можете скопировать результат напрямую в сообщения коммитов, комментарии к code review или баг-репорты.
🌐
Без регистрации и установки
Работает на любом устройстве с браузером. Не нужны аккаунт, расширение или настольное приложение. Откройте страницу и начинайте сравнение.

Сценарии использования text diff

Фронтенд-разработка
Сравнивайте минифицированный CSS или HTML до и после сборки, чтобы выявить непреднамеренные изменения в генерируемой разметке или стилях.
Бэкенд-разработка
Сравнивайте ответы API в разных окружениях (staging и production), чтобы убедиться, что деплой не внёс неожиданных изменений в данные.
DevOps и инфраструктура
Сравнивайте манифесты Kubernetes, Terraform-планы или конфигурации Nginx перед применением изменений на работающем кластере или сервере.
QA и тестирование
Проверяйте соответствие вывода тестов ожидаемым эталонам, сравнивая фактический результат с сохранённым снимком состояния.
Инженерия данных
Сравнивайте заголовки CSV или определения схем SQL при миграциях баз данных, чтобы подтвердить корректность добавления или переименования столбцов.
Обучение и учебные задания
Сравнивайте своё решение с эталонной реализацией построчно, чтобы найти, где ваша логика расходится с ожидаемым подходом.

Сравнение форматов вывода diff

Инструменты diff создают вывод в нескольких форматах. В таблице ниже приведены наиболее распространённые из них, что их генерирует и когда каждый из них полезен.

ФорматИнструмент / ИсточникОписание
Unified diffdiff -u / git diffPrefixes lines with + / - / space; includes @@ hunk headers
Side-by-sidediff -y / sdiffTwo columns, changed lines aligned horizontally
Context diffdiff -cShows changed lines with surrounding context, marked with ! / + / -
HTML diffPython difflibColor-coded HTML table with inline change highlights
JSON PatchRFC 6902Array of add/remove/replace operations on a JSON document

Как работает построчный diff: алгоритм LCS

Большинство инструментов построчного сравнения, включая данный, используют алгоритм Наибольшей Общей Подпоследовательности (LCS). LCS находит наибольший набор строк, присутствующих в обоих текстах в одном относительном порядке, без требования их непрерывности. Строки, не входящие в LCS, являются фактическими различиями.

Стандартный алгоритм LCS использует динамическое программирование и выполняется за O(m × n), где m и n — количество строк в двух текстах. Для больших файлов оптимизированные варианты, такие как алгоритм Myers' diff (используемый Git), сокращают это до O(n + d²), где d — количество различий, что обеспечивает высокую скорость при большом числе совпадающих строк.

1. Построение DP-таблицы
Создаётся матрица (m+1) × (n+1). Для каждой пары строк сохраняется длина наибольшей общей подпоследовательности, найденной к данному моменту. Совпадающие строки увеличивают диагональное значение на 1.
2. Обратный проход
Проход от правого нижнего угла матрицы к позиции (0,0). Диагональные переходы по совпадающим строкам дают записи «неизменено». Горизонтальные или вертикальные переходы дают записи «добавлено» или «удалено».
3. Формирование вывода
Каждая запись преобразуется в строку отображения: неизменённые строки — без префикса, добавленные — с +, удалённые — с -. Применяется цветовое кодирование для наглядности.

Примеры кода

Реализации построчного сравнения текстов на JavaScript, Python, Go и в командной строке. Каждый пример выдаёт вывод в стиле unified diff.

JavaScript
// Line-by-line diff using the LCS algorithm
function diffLines(a, b) {
  const linesA = a.split('\n')
  const linesB = b.split('\n')

  // Build LCS table
  const m = linesA.length, n = linesB.length
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0))
  for (let i = 1; i <= m; i++)
    for (let j = 1; j <= n; j++)
      dp[i][j] = linesA[i-1] === linesB[j-1]
        ? dp[i-1][j-1] + 1
        : Math.max(dp[i-1][j], dp[i][j-1])

  // Backtrack to produce diff
  const result = []
  let i = m, j = n
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && linesA[i-1] === linesB[j-1]) {
      result.unshift({ type: 'equal', text: linesA[i-1] }); i--; j--
    } else if (j > 0 && (i === 0 || dp[i][j-1] >= dp[i-1][j])) {
      result.unshift({ type: 'add', text: linesB[j-1] }); j--
    } else {
      result.unshift({ type: 'remove', text: linesA[i-1] }); i--
    }
  }
  return result
}

const diff = diffLines("alpha\nbeta\ngamma", "alpha\nbeta changed\ngamma\ndelta")
// → [
//   { type: 'equal',  text: 'alpha' },
//   { type: 'remove', text: 'beta' },
//   { type: 'add',    text: 'beta changed' },
//   { type: 'equal',  text: 'gamma' },
//   { type: 'add',    text: 'delta' }
// ]
Python
import difflib

text_a = """alpha
beta
gamma""".splitlines()

text_b = """alpha
beta changed
gamma
delta""".splitlines()

# Unified diff (same format as git diff)
for line in difflib.unified_diff(text_a, text_b, fromfile='a.txt', tofile='b.txt', lineterm=''):
    print(line)
# --- a.txt
# +++ b.txt
# @@ -1,3 +1,4 @@
#  alpha
# -beta
# +beta changed
#  gamma
# +delta

# HTML side-by-side diff
d = difflib.HtmlDiff()
html = d.make_file(text_a, text_b, fromdesc='Original', todesc='Modified')
Go
package main

import (
	"fmt"
	"strings"
)

// Minimal LCS-based line diff
func diffLines(a, b string) {
	la := strings.Split(a, "\n")
	lb := strings.Split(b, "\n")
	m, n := len(la), len(lb)

	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			if la[i-1] == lb[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else if dp[i-1][j] >= dp[i][j-1] {
				dp[i][j] = dp[i-1][j]
			} else {
				dp[i][j] = dp[i][j-1]
			}
		}
	}

	var result []string
	i, j := m, n
	for i > 0 || j > 0 {
		if i > 0 && j > 0 && la[i-1] == lb[j-1] {
			result = append([]string{" " + la[i-1]}, result...)
			i--; j--
		} else if j > 0 && (i == 0 || dp[i][j-1] >= dp[i-1][j]) {
			result = append([]string{"+" + lb[j-1]}, result...)
			j--
		} else {
			result = append([]string{"-" + la[i-1]}, result...)
			i--
		}
	}

	for _, line := range result {
		fmt.Println(line)
	}
}

// Output:
//  alpha
// -beta
// +beta changed
//  gamma
// +delta
CLI (diff / git)
# Compare two files with unified diff (3 lines of context)
diff -u original.txt modified.txt

# Git diff between working tree and last commit
git diff HEAD -- file.txt

# Git diff between two branches
git diff main..feature -- src/

# Side-by-side diff in the terminal
diff -y --width=120 original.txt modified.txt

# Color-coded diff (requires colordiff)
diff -u original.txt modified.txt | colordiff

Часто задаваемые вопросы

В чём разница между построчным diff и посимвольным diff?
Построчный diff сравнивает текст строка за строкой: если хотя бы один символ в строке изменился, вся строка помечается как удалённая, а новая версия — как добавленная. Посимвольный diff (или word diff) работает на более детальном уровне, выделяя конкретные символы или слова, изменившиеся внутри строки. Построчный diff является стандартом для code review; посимвольный diff более полезен при редактировании текста.
Как работает git diff внутри?
По умолчанию Git использует алгоритм Myers' diff, который находит кратчайший скрипт правок (минимальное количество вставок и удалений) между двумя файлами. Git сохраняет результат в формате unified diff с заголовками @@ hunk, указывающими номера строк. При запуске git diff Git сравнивает рабочее дерево с индексом (областью подготовки), а не с последним коммитом, если явно не передать HEAD.
Можно ли сравнивать бинарные файлы с помощью инструмента text diff?
Нет. Инструменты text diff разбивают входные данные по символам новой строки и сравнивают строки. Бинарные файлы содержат произвольные последовательности байт, которые дают бессмысленные разбивки на строки. Для бинарного сравнения используйте hex diff или сравнение контрольных сумм файлов (sha256sum для обоих файлов).
Отправляются ли мои данные на сервер при использовании этого инструмента?
Нет. Инструмент работает полностью в вашем браузере. Вычисление diff использует JavaScript-реализацию LCS, выполняемую на стороне клиента. Никаких сетевых запросов с содержимым вашего текста не выполняется. Вы можете убедиться в этом, открыв вкладку Network в инструментах разработчика браузера во время работы с инструментом.
Что такое формат unified diff?
Unified diff — это формат вывода, используемый командами diff -u и git diff. Он отображает изменённые строки с префиксами - (удалено) и + (добавлено), которым предшествуют заголовки @@, указывающие диапазоны номеров строк в обоих файлах. Контекстные строки (неизменённые) имеют префикс в виде пробела. Unified diff является наиболее распространённым форматом для патчей, pull request'ов и инструментов code review.
Как сравнивать большие файлы с большим количеством различий?
Для файлов с тысячами строк браузерный инструмент работает, но может замедлиться, так как алгоритм LCS масштабируется квадратично. Для очень больших diff'ов инструменты командной строки, такие как diff или git diff, работают быстрее благодаря оптимизированным реализациям на C и возможности потоковой передачи вывода. Вы также можете скрыть неизменённые строки (отключив «Показывать неизменённые строки» в данном инструменте), чтобы сосредоточиться только на изменениях.
Что такое трёхсторонний merge diff?
Трёхсторонний merge сравнивает две изменённые версии файла с их общим предком (базой). Git использует его при git merge и git rebase. Он определяет изменения, внесённые в каждой ветке относительно базы, и объединяет их. Если обе ветки изменяют одну и ту же строку, Git сообщает о конфликте слияния. Трёхсторонний merge требует трёх входных данных, тогда как стандартный diff — лишь двух.