Trình kiểm tra Diff văn bản
So sánh hai văn bản và tìm sự khác biệt ngay lập tức.
Về so sánh văn bản
Công cụ diff này sử dụng thuật toán dãy con chung dài nhất (LCS) để so sánh hai văn bản dòng theo dòng. Các bổ sung được làm nổi bật màu xanh lá và các xóa màu đỏ. Chọn chế độ xem «Inline» để xem tất cả các thay đổi trên một cột duy nhất, hoặc «Cạnh nhau» để so sánh các văn bản gốc và đã sửa đổi song song.
Các sử dụng phổ biến
- So sánh các thay đổi mã trước khi commit
- Đánh giá các tài liệu hoặc hợp đồng đã sửa đổi
- Kiểm tra các sự khác biệt về dịch thuật
- Xác thực các thay đổi của tệp cấu hình
- So sánh các phản hồi API
Câu hỏi thường gặp
Nó có so sánh ký tự theo ký tự không?
Công cụ này so sánh dòng theo dòng. Nếu một dòng có một thay đổi nhỏ nhất (kể cả một ký tự duy nhất), toàn bộ dòng được đánh dấu là đã sửa đổi. Đây là cùng cách tiếp cận được sử dụng bởi Git và hầu hết các công cụ diff.
Có giới hạn kích thước không?
Không có giới hạn nghiêm ngặt, nhưng các văn bản rất lớn (hơn 10.000 dòng) có thể mất một chút thời gian để xử lý, vì so sánh chạy hoàn toàn trong trình duyệt của bạn.
Ý nghĩa thực sự của «diff»
Diff mô tả cách biến một văn bản thành văn bản khác bằng tập hợp chèn và xóa nhỏ nhất. Có vô số cách để làm điều đó, cách tầm thường là «xóa mọi ký tự của A rồi chèn mọi ký tự của B.» Một diff hữu ích là script chỉnh sửa ngắn nhất, là mặt đối ngẫu của bài toán Dãy Con Chung Dài Nhất: tìm dãy dài nhất các dòng xuất hiện (theo thứ tự) trong cả hai văn bản, và tất cả phần còn lại là thêm hoặc xóa. Hầu hết các thuật toán diff là các thuật toán LCS dưới một cái tên khác.
Một điểm tinh tế: dãy con chung dài nhất không phải là chuỗi con chung dài nhất. Dãy con bảo toàn thứ tự nhưng không cần liền kề, vì vậy «ABCDE» và «AXBYCZD» chia sẻ dãy con «ABCD» mặc dù không có chuỗi liên tiếp nào gồm ba ký tự xuất hiện trong cả hai.
Lịch sử ngắn gọn các thuật toán diff
Chương trình so sánh file được dùng rộng rãi đầu tiên là Unix diff được viết bởi Douglas McIlroy tại Bell Labs, với thuật toán cơ bản được công bố bởi James W. Hunt và McIlroy trong Báo cáo Kỹ thuật Khoa học Máy tính của Bell Labs #41 vào tháng 6 năm 1976. Bản thân McIlroy mô tả quá trình phát triển bốn tháng là «một nỗ lực tuyệt vọng.» Phương pháp Hunt-McIlroy băm mọi dòng của file B, lập chỉ mục nơi xuất hiện mỗi dòng duy nhất, và tìm kiếm chuỗi tăng đơn điệu dài nhất của các khớp. Nó được tích hợp trong Unix Version 6 và về cơ bản vẫn là cùng thuật toán trong /usr/bin/diff trên hầu hết các hệ thống Unix trong nhiều thập kỷ.
Eugene W. Myers công bố tiêu chuẩn công nghiệp hiện đại vào năm 1986, «An O(ND) Difference Algorithm and Its Variations» trong tạp chí Algorithmica Vol. 1, No. 2. Myers tái định nghĩa LCS thành bài toán đường đi ngắn nhất qua một đồ thị chỉnh sửa: đặt A dọc theo trục trên và B dọc theo trục bên, vẽ đường chéo bất cứ khi nào hai dòng khớp, và tìm kiếm đường đi từ trên-trái đến dưới-phải với ít cạnh không chéo nhất. Mỗi cạnh không chéo là một lần chèn hoặc xóa; mỗi đường chéo là miễn phí. Quan trọng hơn, thuật toán chạy trong thời gian O((N+M)·D) trong đó D là kích thước của script chỉnh sửa, tức là nó chạy nhanh hơn khi các file càng giống nhau. Đối với các diff kiểm soát phiên bản thông thường, D rất nhỏ so với N+M, vì vậy Myers nhanh hơn đáng kể so với trường hợp xấu nhất O(N·M) trong thực tế. Bài báo của ông cũng bao gồm cải tiến không gian tuyến tính sử dụng thủ thuật «middle snake» chia-để-trị. Thuật toán Myers là thuật toán mặc định cho GNU diff, git diff, Mercurial, Subversion, jsdiff, difflib của Python và hầu hết các trình xem diff GUI.
Bram Cohen (sau này nổi tiếng hơn với tư cách là người phát minh ra BitTorrent) đã thiết kế patience diff cho hệ thống kiểm soát phiên bản Bazaar vào khoảng năm 2002. Vấn đề thúc đẩy: Myers diff căn chỉnh bất kỳ dòng khớp nào, bao gồm cả những dòng rất phổ biến, vì vậy việc di chuyển một hàm trong C có thể tạo ra diff nhiễu trong đó dấu ngoặc đóng của hàm được di chuyển căn chỉnh với dấu ngoặc đóng của một hàm không liên quan. Patience diff neo diff trên các dòng xuất hiện đúng một lần trong mỗi file (thường là chữ ký hàm, tiêu đề chú thích, thông điệp log đặc trưng), tính LCS chỉ của các neo đó bằng patience sort, và đệ quy trên các khoảng trống. Kết quả căn chỉnh trên các dòng có ý nghĩa và dễ đọc hơn cho mã nguồn. Git hỗ trợ nó qua git diff --patience. Histogram diff là cải tiến được thêm vào git khoảng năm 2010 xây dựng histogram tần suất xuất hiện dòng và ưu tiên các neo tần suất thấp thay vì các neo hoàn toàn duy nhất; từ git 2.45 trở đi nó là mặc định cho nhiều cấu hình.
Levenshtein so với LCS
Một khái niệm liên quan đáng phân biệt. Khoảng cách Levenshtein (Vladimir Levenshtein, 1965) đếm số lần chèn, xóa và thay thế một ký tự tối thiểu để biến một chuỗi thành chuỗi khác. Diff dựa trên LCS có liên quan chặt chẽ nhưng hơi khác: diff cổ điển coi một «thay đổi» là một cặp xóa-rồi-chèn thay vì một lần thay thế đơn lẻ, vì điều đó phù hợp hơn với các file theo dòng. Levenshtein trả lời «những chuỗi này khác nhau như thế nào?» và cho bạn một con số; LCS trả lời «điều gì cụ thể đã thay đổi?» và cho bạn script chỉnh sửa thực tế. Bộ kiểm tra chính tả và các tính năng «bạn có muốn nói...?» muốn cái đầu; các công cụ diff muốn cái sau. Biến thể Damerau-Levenshtein (1964) mở rộng Levenshtein với toán tử hoán vị để phát hiện lỗi đánh máy.
Độ chi tiết: dòng, từ và ký tự
Diff có thể được tính ở các độ chi tiết khác nhau, mỗi loại có sự đánh đổi:
- Cấp dòng: mỗi dòng là một token; hai dòng hoặc khớp chính xác hoặc không. Nhanh, ánh xạ rõ ràng lên cách lập trình viên chỉnh sửa code, tạo ra các bản vá ngắn gọn mà
patch(1)có thể áp dụng. Chi phí: định dạng lại một ký tự trong dòng 200 ký tự đánh dấu toàn bộ dòng là đã thay đổi. Mặc định chodiff, git, SVN và công cụ này. - Cấp từ: mỗi token được phân cách bởi khoảng trắng là một đơn vị, vì vậy một dòng thay đổi một từ chỉ hiển thị từ đó được đánh dấu. Xuất sắc cho văn xuôi, hợp đồng, bản thảo blog. Được dùng bởi
git diff --word-diff, chế độ «Suggesting» của Google Docs, «Track Changes» của Microsoft Word, chế độ compare-words của Diffchecker. - Cấp ký tự: mỗi điểm mã Unicode là một đơn vị. Bắt lỗi đánh máy một ký tự, lý tưởng cho việc đánh dấu pháp lý nơi một từ có thể đảo ngược ý nghĩa. Chậm trên đầu vào lớn và đôi khi không đọc được cho các chỉnh sửa dài.
Một mẫu phổ biến trong các UI diff hiện đại là tính diff ở cấp dòng trước, sau đó đối với bất kỳ cặp «láng giềng đã xóa/thêm» nào, chạy diff cấp từ thứ cấp chỉ trên hai dòng đó và đánh dấu các thay đổi trong dòng. Đó chính xác là cách cả chế độ xem PR của GitHub và thư viện diff-match-patch hoạt động.
Ba chế độ hiển thị diff bạn sẽ thấy
- Unified diff (định dạng của mọi file
.patch), các hunk được giới thiệu bởi@@ -oldStart,oldLen +newStart,newLen @@với ba dòng ngữ cảnh ở trên và dưới theo mặc định. Bắt nguồn từ cờ-ucủa GNU diff của Wayne Davison vào khoảng năm 1990. Gọn gàng và đọc được bởi máy quapatch(1), đó là lý do tại sao git, các patch email kiểu kernel và SVN đều dùng nó. - Side-by-side: bản gốc ở bên trái, bản chỉnh sửa ở bên phải, được căn chỉnh từng dòng.
diff -ytạo ra phiên bản ASCII nguyên thủy; Beyond Compare, Meld, WinMerge và chế độ xem «Side by Side» ở trên đều dùng định dạng này. Tốt nhất để xem xét trực quan và đọc thử văn xuôi. - Inline (xếp chồng): một cột duy nhất nơi các dòng đã xóa và thêm được xen kẽ. Chế độ xem PR của GitHub mặc định là cái này. Định dạng tiết kiệm không gian nhất cho màn hình hẹp và chỉnh sửa nhỏ.
Các biến thể git diff hữu ích
git diff: index so với working tree.git diff --staged: HEAD so với index.git diff main..feature: các đầu nhánh.main...featureso sánh với merge base, những gì PR sẽ đưa vào.git diff --word-diffvà--color-words: diff cấp từ thân thiện với markup và tô màu từng màu.git diff --no-index a.txt b.txt: hoạt động trên các file ngoài bất kỳ repo git nào. Tiện như một công cụ diff độc lập đơn giản với các thuật toán patience hoặc histogram.git diff -w: bỏ qua tất cả khoảng trắng (tương ứng với ô checkbox «Ignore whitespace» ở trên).git diff --histogram: chuyển thuật toán mỗi lệnh.
Khi nào bạn cần dùng browser diff
- Xem xét code. Dán hai phiên bản của một file, quét xem gì đã thay đổi, quyết định xem thay đổi có hợp lý không.
- Xem xét code do AI chỉnh sửa. So sánh bản gốc của bạn với phiên bản được LLM tái cấu trúc để phát hiện sai lệch hoặc ảo giác.
- Đánh dấu hợp đồng pháp lý. Xác nhận rằng chỉ các điều khoản đã đồng ý thay đổi giữa phiên bản 3 và phiên bản 4.
- Kiểm tra chất lượng dịch thuật. So sánh hai bản dịch tiếng Pháp của cùng đoạn văn, hoặc so sánh các file bản địa hóa cũ và mới khi chỉ một số chuỗi nhất định nên thay đổi.
- Drift cấu hình. So sánh
nginx.conftừ môi trường prod với staging. - So sánh schema SQL. Xuất các câu lệnh
CREATE TABLEtừ hai cơ sở dữ liệu và so sánh chúng để tìm chỉ mục bị thiếu hoặc drift kiểu cột. - Drift spec. So sánh hai phiên bản của OpenAPI / Swagger YAML để xác minh các thay đổi gây phá vỡ được ghi chép.
- Bản thảo blog Markdown. Phát hiện những gì đồng tác giả đã chỉnh sửa.
- Kiểm tra tuân thủ. So sánh chính sách quyền riêng tư theo từng tháng cho các cơ quan quản lý.
- Pháp y file lock. So sánh
package-lock.jsonhoặcyarn.lockđể hiểu một lần nâng cấp dependency.
Giới hạn trung thực
- Diff văn bản thuần túy không có nhận thức ngữ nghĩa. Đổi tên
foothànhbartrên toàn bộ file trông giống hệt về chi phí với một lần viết lại hoàn toàn không liên quan. Các công cụ như difftastic phân tích AST của ngôn ngữ và tạo ra các diff có cấu trúc («function added,» «argument reordered»). Trang này thì không, nó là diff dòng LCS thuần túy. - Khoảng trắng và ký tự kết thúc dòng gây ra diff giả. CRLF so với LF, khoảng trắng cuối dòng, tab so với dấu cách. Nút chuyển «Ignore whitespace» ở trên tương ứng với
-wcủa git. - File nhị phân không phù hợp. Diff là khái niệm văn bản. Đối với binary diff có các công cụ như
bsdiff,xdelta, hoặc VBinDiff. - Nhạy cảm với thứ tự. Diff dòng giả định các tài liệu được chỉnh sửa từng dòng. Đối với các CSV không được sắp xếp hoặc sắp xếp lại các key của đối tượng JSON, bạn nên sắp xếp hoặc chuẩn hóa trước.
- Phát hiện di chuyển. Diff LCS cổ điển không nhận ra rằng một hàm đã được di chuyển trong một file, nó hiển thị việc di chuyển dưới dạng xóa cộng chèn. Beyond Compare, Meld và difftastic cố gắng phát hiện di chuyển; trang này thì không.
Thêm câu hỏi
Tại sao trang này không hiển thị đánh dấu cấp ký tự bên trong các dòng đã thay đổi?
Vì lần diff cấp dòng là đủ cho hầu hết các trường hợp sử dụng và chạy nhanh hơn nhiều trên đầu vào dài. Đối với đánh dấu từng từ trong dòng, cải tiến hai bước là thực hành tiêu chuẩn nhưng thêm độ trễ. Nếu sự khác biệt toàn bộ dòng quan trọng với bạn (đánh dấu pháp lý, đọc thử văn xuôi), một công cụ cấp từ chuyên dụng (bao gồm git diff --word-diff trên dòng lệnh) phù hợp hơn.
Sự khác biệt giữa unified diff và side-by-side là gì?
Unified diff là định dạng gọn gàng, đọc được bởi máy mà mọi file .patch sử dụng, ba dòng ngữ cảnh, các hunk được đánh dấu bằng @@. Side-by-side hiển thị hai phiên bản trong các cột song song, dễ nhìn hơn để xem xét trực quan nhưng chiếm nhiều không gian ngang hơn. Unified là thứ bạn sẽ email hoặc commit; side-by-side là thứ bạn sẽ đọc trên màn hình rộng.
Có thuật toán nào tốt hơn Myers cho mã nguồn không?
Đối với mã nguồn có các hàm được di chuyển hoặc sắp xếp lại lớn, có, patience diff (Bram Cohen, 2002) và histogram diff (git, khoảng năm 2010) được thiết kế để căn chỉnh trên các dòng duy nhất có ý nghĩa thay vì các dòng phổ biến, tạo ra đầu ra dễ đọc hơn. Cả hai đều có thể dùng qua git diff --patience và git diff --histogram. Các công cụ hiểu AST như difftastic đi xa hơn và phân tích cấu trúc ngôn ngữ thực tế.
Có an toàn để dán văn bản bí mật ở đây không?
Có, diff chạy hoàn toàn trong trình duyệt của bạn bằng cách sử dụng triển khai LCS JavaScript. Không có gì được tải lên, không có phân tích trên đầu vào, không có nhật ký phía máy chủ về văn bản. Đây là danh mục công cụ diff duy nhất an toàn cho NDA, mã nguồn nội bộ, hoặc các điều khoản hợp đồng chưa phát hành; các dịch vụ diff trên đám mây thấy bất cứ thứ gì bạn dán vào.