Text Diff

Порівнюйте два тексти поруч і виділяйте відмінності рядок за рядком

Спробувати приклад

Текст A

Текст B

Працює локально · Безпечно вставляти секрети
Працює локально · Безпечно вставляти секрети

Що таке Text Diff?

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

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

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

Навіщо використовувати онлайн-інструмент Text Diff?

Порівняння текстів у терміналі потребує встановлення утиліт diff та роботи з прапорами командного рядка. Браузерний інструмент diff повністю усуває це ускладнення.

Миттєве порівняння
Вставте два текстові блоки і побачте виділені відмінності негайно. Жодного створення файлів, жодних команд для запам'ятовування, жодного виведення для розбору.
🔒
Обробка з пріоритетом конфіденційності
Усе порівняння відбувається у вашому браузері за допомогою JavaScript. Ваш текст ніколи не покидає пристрій — це важливо при порівнянні конфігураційних файлів, облікових даних або власного коду.
📋
Готове до копіювання виведення
Вивід diff використовує стандартні префікси + / -, що відповідають формату unified diff. Ви можете скопіювати результат безпосередньо до повідомлень комітів, коментарів перевірки коду або звітів про помилки.
🌐
Без реєстрації та встановлення
Працює на будь-якому пристрої з браузером. Без реєстрації, без розширення, без настільного застосунку. Відкрийте сторінку і починайте порівнювати.

Сценарії використання 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

Як працює порівняння рядків: алгоритм LCS

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

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

1. Побудова таблиці DP
Створюється матриця розміром (m+1) x (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

Часті запитання

У чому різниця між порівнянням рядків і порівнянням символів?
Порівняння рядків аналізує текст рядок за рядком: якщо в рядку змінюється будь-який символ, весь рядок позначається як видалений, а нова версія — як доданий. Порівняння символів (або порівняння слів) працює на дрібнішому рівні, позначаючи конкретні символи або слова, що змінились у рядку. Порівняння рядків є стандартом для перевірки коду; порівняння символів більш корисне для редагування прози.
Як git diff працює всередині?
Git за замовчуванням використовує алгоритм Myers' diff, який знаходить найкоротший скрипт редагування (мінімальна кількість вставок і видалень) між двома файлами. Git зберігає результат у форматі unified diff із заголовками ханків @@ із зазначенням номерів рядків. Коли ви виконуєте git diff, Git порівнює робоче дерево з індексом (область підготовки), а не з останнім комітом, якщо ви явно не передасте HEAD.
Чи можна порівнювати бінарні файли за допомогою інструменту text diff?
Ні. Інструменти text diff розбивають введення за символами нового рядка та порівнюють рядки. Бінарні файли містять довільні послідовності байтів, що призводять до безглуздих розбиттів на рядки. Для бінарного порівняння використовуйте інструмент hex diff або порівняння контрольних сум файлів (sha256sum для обох файлів).
Чи надсилаються мої дані на сервер при використанні цього інструменту?
Ні. Цей інструмент працює повністю у вашому браузері. Обчислення diff використовує реалізацію LCS на JavaScript, що виконується на стороні клієнта. Жодних мережевих запитів із вашим текстовим вмістом не надсилається. Ви можете перевірити це, відкривши вкладку «Мережа» у браузері під час використання інструменту.
Що таке формат unified diff?
Unified diff — це формат виведення, що використовується командами diff -u та git diff. Він показує змінені рядки з префіксами - (видалено) та + (додано), яким передують заголовки @@ із зазначенням діапазонів номерів рядків в обох файлах. Контекстні рядки (незмінені) мають пробіл як префікс. Unified diff є найпоширенішим форматом для патчів, запитів на злиття та інструментів перевірки коду.
Як порівнювати великі файли з багатьма відмінностями?
Для файлів із тисячами рядків браузерний інструмент працює, але може сповільнитися, оскільки алгоритм LCS масштабується квадратично. Для дуже великих diff-ів інструменти командного рядка, як-от diff або git diff, швидші завдяки оптимізованим реалізаціям на C та потоковому виведенню. Також можна відфільтрувати незмінені рядки (вимкнути «Показувати незмінені рядки» в цьому інструменті), щоб зосередитись лише на змінах.
Що таке тривосторонній злиття diff?
Тривосторонній злиття порівнює дві змінені версії файлу з їхнім спільним предком (базою). Git використовує це під час git merge та git rebase. Визначаються зміни, внесені в кожній гілці відносно бази, і вони об'єднуються. Коли обидві гілки змінюють один і той самий рядок, Git повідомляє про конфлікт злиття. Тривостороннє злиття потребує трьох вхідних даних, тоді як стандартний diff — лише двох.