Text Diff
Bandingkan dua teks berdampingan dan sorot perbedaan baris per baris
Teks A
Teks B
Apa Itu Text Diff?
Text diff (singkatan dari "difference" atau perbedaan) adalah hasil perbandingan dua blok teks yang mengidentifikasi baris mana yang ditambahkan, dihapus, atau tidak berubah. Konsep ini berasal dari utilitas diff Unix, yang pertama kali dirilis pada tahun 1974 sebagai bagian dari Version 5 Unix. Saat ini, text diff menjadi tulang punggung sistem kontrol versi seperti Git, di mana setiap commit menyimpan diff alih-alih salinan lengkap setiap file.
Algoritma diff mencari Longest Common Subsequence (LCS) antara dua urutan baris. Baris yang ada di LCS ditandai sebagai tidak berubah. Baris yang ada di teks asli tetapi tidak di LCS ditandai sebagai dihapus. Baris yang ada di teks yang dimodifikasi tetapi tidak di LCS ditandai sebagai ditambahkan. Hasilnya adalah sekumpulan perubahan minimal yang diperlukan untuk mengubah satu teks menjadi teks lainnya.
Keluaran diff hadir dalam beberapa format. Unified diff (format default untuk git diff) memberi awalan tanda minus pada baris yang dihapus dan tanda plus pada baris yang ditambahkan. Diff berdampingan menampilkan kedua teks dalam kolom paralel. Alat ini menggunakan perbandingan baris per baris dengan keluaran berkode warna: hijau untuk penambahan, merah untuk penghapusan, dan netral untuk baris yang tidak berubah. Baris yang tidak berubah ditampilkan tanpa awalan secara default, tetapi dapat disembunyikan agar hanya fokus pada perubahan.
Mengapa Menggunakan Alat Text Diff Online?
Membandingkan teks di terminal memerlukan instalasi utilitas diff dan pemahaman flag baris perintah. Alat diff berbasis browser menghilangkan hambatan tersebut sepenuhnya.
Kasus Penggunaan Text Diff
Perbandingan Format Keluaran Diff
Alat diff menghasilkan keluaran dalam beberapa format. Tabel berikut merangkum format yang paling umum, apa yang menghasilkannya, dan kapan masing-masing berguna.
| Format | Alat / Sumber | Deskripsi |
|---|---|---|
| Unified diff | diff -u / git diff | Prefixes lines with + / - / space; includes @@ hunk headers |
| Side-by-side | diff -y / sdiff | Two columns, changed lines aligned horizontally |
| Context diff | diff -c | Shows changed lines with surrounding context, marked with ! / + / - |
| HTML diff | Python difflib | Color-coded HTML table with inline change highlights |
| JSON Patch | RFC 6902 | Array of add/remove/replace operations on a JSON document |
Cara Kerja Line Diff: Algoritma LCS
Sebagian besar alat line-diff, termasuk alat ini, menggunakan algoritma Longest Common Subsequence (LCS). LCS menemukan kumpulan baris terbesar yang muncul di kedua teks dalam urutan relatif yang sama, tanpa mensyaratkan bahwa baris-baris tersebut berdekatan. Baris yang tidak ada di LCS adalah perbedaan yang sebenarnya.
Algoritma LCS standar menggunakan dynamic programming dan berjalan dalam waktu O(m x n), di mana m dan n adalah jumlah baris dari kedua teks. Untuk file besar, varian yang dioptimalkan seperti algoritma diff Myers (digunakan oleh Git) mereduksinya menjadi O(n + d^2) di mana d adalah jumlah perbedaan, sehingga lebih cepat ketika sebagian besar baris sama.
Contoh Kode
Implementasi perbandingan teks baris per baris dalam JavaScript, Python, Go, dan baris perintah. Setiap contoh menghasilkan keluaran berformat unified-style diff.
// 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' }
// ]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')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# 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