टेक्स्ट डिफ़ चेकर
अपना मूल और संशोधित टेक्स्ट पेस्ट करें, फिर यह देखने के लिए तुलना करें पर क्लिक करें कि क्या बदला। जोड़ी गई पंक्तियाँ हरे रंग में, हटाई गई पंक्तियाँ लाल रंग में, अपरिवर्तित पंक्तियाँ स्लेटी रंग में हाइलाइट होती हैं। पूरी तरह आपके ब्राउज़र में चलता है — कुछ भी अपलोड नहीं होता।
दोनों फ़ील्ड में टेक्स्ट डालें और तुलना करें पर क्लिक करें।
टेक्स्ट डिफ़ टूल का उपयोग कैसे करें
- अपना मूल टेक्स्ट बाएँ बॉक्स में और संशोधित टेक्स्ट दाएँ बॉक्स में पेस्ट करें।
- यदि आप केवल सामग्री के बदलाव की परवाह करते हैं, इंडेंटेशन की नहीं, तो वैकल्पिक रूप से आगे/पीछे की खाली जगह अनदेखा करें पर टिक करें।
- तुलना करें पर क्लिक करें। डिफ़ नीचे दिखती है: हरी पंक्तियाँ जोड़ी गईं, लाल पंक्तियाँ हटाई गईं, बिना निशान वाली पंक्तियाँ अपरिवर्तित हैं।
टेक्स्ट डिफ़ क्या है?
एक "डिफ़" (difference का संक्षिप्त रूप) उन न्यूनतम बदलावों का समूह दिखाती है जो किसी टेक्स्ट के एक संस्करण को दूसरे में बदलने के लिए ज़रूरी हैं। डिफ़ का उपयोग वर्शन कंट्रोल (git, SVN), कोड समीक्षा, दस्तावेज़ तुलना (कॉन्ट्रैक्ट संस्करण, लेख संशोधन) और डेटा सत्यापन (एक्सपोर्ट की गई CSV फ़ाइलों, कॉन्फ़िग फ़ाइलों की तुलना) में होता है।
यहाँ इस्तेमाल किया गया एल्गोरिथ्म LCS (सबसे लंबा साझा उपअनुक्रम — Longest Common Subsequence)
है — यह पंक्तियों का वह सबसे लंबा अनुक्रम खोजता है जो दोनों टेक्स्ट में साझा है, फिर बाक़ी सब कुछ जोड़ा गया
या हटाया गया चिह्नित करता है। यह वही एल्गोरिथ्म है जो diff(1), git diff और
अधिकांश कोड-समीक्षा डिफ़ व्यूज़ को शक्ति देता है।
Hunt और McIlroy से Myers तक: diff का एक संक्षिप्त इतिहास
diff यूटिलिटी Bell Labs में लिखी गई थी और 1970 के दशक के मध्य में Unix के साथ आई। इसका
एल्गोरिथ्म जून 1976 में James W. Hunt और M. Douglas McIlroy ने Bell Labs Computing Science Technical
Report "An Algorithm for Differential File Comparison." के रूप में प्रकाशित किया। Hunt ने
प्रोटोटाइप बनाया; McIlroy ने उसे निखारा। यह पहले ग़ैर-अनुमानी (non-heuristic) फ़ाइल-तुलना टूलों में से
एक था, और इसने समस्या को उसी तरह परिभाषित किया जैसे हर डिफ़ टूल आज भी करता है: दोनों फ़ाइलों का
सबसे लंबा साझा उपअनुक्रम (LCS) खोजो, फिर उस साझा रीढ़ के बाहर की हर चीज़ को जोड़ा गया या
हटाया गया बताओ।
एक दशक बाद, 1986 में, Eugene W. Myers ने Algorithmica जर्नल में "An O(ND) Difference Algorithm and Its Variations" प्रकाशित किया। Myers एल्गोरिथ्म तेज़ है और समझने में आसान है, और यह Git तथा असंख्य एडिटरों और समीक्षा टूलों में डिफ़ॉल्ट बन गया। इस पेज पर आप जो डिफ़ देखते हैं वह उसी LCS नींव पर टिकी है जिसे उन दोनों शोध-पत्रों ने स्थापित किया।
सबसे छोटी एडिट स्क्रिप्ट के रूप में डिफ़: Myers एल्गोरिथ्म
Myers ने तुलना को एक एडिट ग्राफ़ (edit graph) पर पथ-खोज (path-finding) की समस्या के रूप में फिर से गढ़ा। कल्पना करें कि मूल फ़ाइल किसी ग्रिड के ऊपर और संशोधित फ़ाइल किनारे पर नीचे रखी है। दाईं ओर बढ़ना एक पंक्ति हटाता है, नीचे बढ़ना एक पंक्ति जोड़ता है, और तिरछा (diagonal) बढ़ना ऐसी पंक्ति रखता है जो दोनों में मेल खाती है। सबसे अच्छी डिफ़ ऊपर-बाएँ कोने से नीचे-दाएँ कोने तक का वह पथ है जो सबसे अधिक तिरछे क़दम लेता है — यानी वह जिसमें सबसे कम जोड़ और हटाव हों। जोड़ और हटाव संचालनों की वह न्यूनतम संख्या सबसे छोटी एडिट स्क्रिप्ट (shortest edit script) है।
यह एल्गोरिथ्म O(ND) समय और स्थान में चलता है, जहाँ N दोनों इनपुट की संयुक्त लंबाई है और D एडिट दूरी है — उस सबसे छोटी एडिट स्क्रिप्ट का आकार। महत्वपूर्ण गुण यह है कि D, न कि N, हावी रहता है: जब दो फ़ाइलें लगभग एक जैसी होती हैं (छोटा D) तो डिफ़ लगभग रैखिक होती है और तुरंत महसूस होती है, जो वर्शन कंट्रोल में ठीक-ठीक आम स्थिति है। उसी शोध-पत्र में वर्णित एक परिशोधन एक विभाजित-करो-और-जीतो (linear-space) तकनीक से मेमोरी को O(N) तक घटा देता है, ताकि बड़ी फ़ाइलें भी मेमोरी न ख़त्म करें।
LCS दूरी बनाम Levenshtein दूरी
"एडिट दूरी (edit distance)" मापों का एक परिवार है, और इनमें से दो के बीच का फ़र्क समझाता है कि डिफ़ आउटपुट वैसा क्यों दिखता है जैसा दिखता है। Levenshtein दूरी किसी एक स्ट्रिंग को दूसरी में बदलने के लिए ज़रूरी एकल-वर्ण जोड़, हटाव और प्रतिस्थापन (substitution) की न्यूनतम संख्या गिनती है। LCS दूरी — पंक्ति-डिफ़ के पीछे का मॉडल — केवल जोड़ और हटाव की अनुमति देती है; कोई प्रतिस्थापन संचालन नहीं होता।
यही एक प्रतिबंध वजह है कि डिफ़ कभी किसी पंक्ति को "जगह पर बदली गई" के रूप में नहीं दिखाती। यह पुरानी पंक्ति
को हटाई गई और नई पंक्ति को जोड़ी गई के रूप में दिखाती है — एक के बजाय दो संचालन। पाठ्यपुस्तक का उदाहरण
kitten को sitting में बदलता है: Levenshtein दूरी 3 है (k को s से बदलें, e को i
से बदलें, g जोड़ें), पर LCS दूरी 5 है, क्योंकि हर प्रतिस्थापन को एक हटाव और एक जोड़ के रूप में व्यक्त करना
पड़ता है। जो टूल पंक्ति के भीतर के बदलाव हाइलाइट करना चाहते हैं वे किसी बदली गई पंक्ति के भीतर एक
दूसरी, बारीक डिफ़ चलाते हैं — पर पंक्ति-स्तर का निर्णय शुद्ध LCS होता है।
Git के चार डिफ़ एल्गोरिथ्म
Git आपको --diff-algorithm से एल्गोरिथ्म चुनने देता है। चारों एक सही डिफ़ बनाते हैं; ये इसमें
भिन्न होते हैं कि किस समान रूप से मान्य संरेखण (alignment) को चुनते हैं, जिससे परिणाम कितना पठनीय
है यह बदल जाता है।
| एल्गोरिथ्म | यह कैसे काम करता है | किसके लिए सबसे अच्छा |
|---|---|---|
| Myers (डिफ़ॉल्ट) | एडिट ग्राफ़ पर लालची (greedy) सबसे-छोटी-एडिट-स्क्रिप्ट खोज। | सामान्य उपयोग; तेज़ और लगभग हमेशा पर्याप्त। |
| Minimal | प्रमाणित रूप से सबसे छोटी डिफ़ की गारंटी के लिए Myers की लालची हूरिस्टिक छोड़ देता है। | छोटे इनपुट जहाँ पूर्ण न्यूनतम मायने रखता है; धीमा। |
| Patience | केवल उन्हीं पंक्तियों का मिलान करता है जो दोनों फ़ाइलों में अद्वितीय हैं, उनके सबसे लंबे बढ़ते उपअनुक्रम पर टिकता है, फिर पुनरावृत्त (recurse) करता है। | रीफ़ैक्टर और पुनर्व्यवस्था, जहाँ Myers ग़लत ब्रेसेस या खाली पंक्तियाँ संरेखित कर देता है। |
| Histogram | patience का एक विस्तार जो कम-आवृत्ति वाली साझा पंक्तियों को भी संभालता है; तुलनीय रूप से तेज़। | एक साफ़ डिफ़ॉल्ट विकल्प; फ़ंक्शन खिसकने पर सुथरा आउटपुट। |
Patience डिफ़ का आविष्कार BitTorrent के निर्माता Bram Cohen ने किया। इसकी अंतर्दृष्टि यह है कि अद्वितीय पंक्तियाँ — किसी बंद ब्रेस के बजाय एक विशिष्ट फ़ंक्शन सिग्नेचर — सबसे अर्थपूर्ण एंकर होती हैं, इसलिए पहले उन्हें संरेखित करना और उनके बीच के अंतरालों में पुनरावृत्त होना ऐसी डिफ़ बनाता है जो इस तरह मेल खाती है जैसे कोई इंसान बदलाव पढ़ता है। Histogram उसी विचार को निखारता है और बड़े रीफ़ैक्टरों पर अक्सर सबसे शांत रहता है।
पंक्ति, शब्द और वर्ण स्तर की सूक्ष्मता
डिफ़ एल्गोरिथ्म टोकनों के एक अनुक्रम की तुलना करते हैं, और आप तय करते हैं कि टोकन क्या है। सबसे आम चुनाव — और वही जो यह टूल इस्तेमाल करता है — पंक्ति है। सोर्स कोड और कॉन्फ़िगरेशन के लिए पंक्तियाँ एक स्वाभाविक इकाई हैं: वे अर्थपूर्ण होती हैं, वर्णों की तुलना में इनकी संख्या बहुत कम होती है (इसलिए O(ND) काम छोटा रहता है), और पंक्ति-स्तर का परिणाम स्कैन करना आसान होता है।
शब्द-स्तर की डिफ़ खाली जगह पर टोकनाइज़ करती है और गद्य के लिए उपयुक्त है, जहाँ किसी लंबे अनुच्छेद में एक-शब्द के बदलाव को पूरी पंक्ति को चिह्नित नहीं करना चाहिए। वर्ण-स्तर की डिफ़ सबसे बारीक स्तर है, जो किसी शब्द के भीतर बदले हुए ठीक-ठीक अक्षरों को हाइलाइट करने के लिए उपयोगी है, पर किसी भी बड़ी चीज़ पर यह गड़बड़ लगती है। कई एडिटर दोनों को जोड़ते हैं: बदले हुए क्षेत्रों को खोजने के लिए एक पंक्ति-डिफ़, फिर सटीक बदलाव हाइलाइट करने के लिए हर क्षेत्र के भीतर एक शब्द- या वर्ण-डिफ़।
यूनिफ़ाइड डिफ़ फ़ॉर्मैट पढ़ना
यूनिफ़ाइड डिफ़ (unified diff) वह फ़ॉर्मैट है जो आप git diff, पैच और पुल
रिक्वेस्ट में देखते हैं। यह दो फ़ाइल हेडरों से शुरू होता है — मूल पर --- और नई पर +++
निशान — और फिर बदलावों को हंक (hunks) में समूहित करता है। हर हंक @@ -l,s +l,s @@
रूप के हेडर से शुरू होता है, जहाँ पहली जोड़ी मूल फ़ाइल में शुरुआती पंक्ति और पंक्ति-गणना है और दूसरी जोड़ी
नई फ़ाइल के लिए वही है।
किसी हंक के भीतर, हर पंक्ति एक एकल-वर्ण उपसर्ग रखती है: अपरिवर्तित संदर्भ (context) पंक्ति के लिए एक
स्पेस, मूल से हटाई गई पंक्ति के लिए एक माइनस, और नई संस्करण में जोड़ी गई
पंक्ति के लिए एक प्लस। डिफ़ॉल्ट रूप से कुछ अपरिवर्तित संदर्भ पंक्तियाँ (आमतौर पर
तीन) हर बदलाव को घेरती हैं ताकि फ़ाइल के थोड़ा खिसक जाने के बाद भी पैच लागू हो सके। पुराना
कॉन्टेक्स्ट डिफ़ (context diff) फ़ॉर्मैट वही जानकारी देता था पर पहले और बाद के ब्लॉक
अलग-अलग छापता था और बदली गई पंक्तियों को ! से चिह्नित करता था; यूनिफ़ाइड फ़ॉर्मैट अधिक संक्षिप्त
है क्योंकि यह दोनों पक्षों को आपस में गूँथ देता है, और यही डिफ़ॉल्ट बन गया है।
तीन-तरफ़ा मर्ज और कॉन्फ़्लिक्ट
एक साधारण दो-तरफ़ा डिफ़ यह नहीं बता सकती कि दो भिन्न फ़ाइलों में से कौन-सी "सही" है। एक
तीन-तरफ़ा मर्ज (three-way merge) साझा पूर्वज (common ancestor) को एक संदर्भ के रूप में
जोड़ता है। diff3 जैसे टूल दोनों संस्करणों की उस आधार से तुलना करते हैं: केवल एक पक्ष पर किया
गया बदलाव स्वतः लागू हो जाता है, जबकि वह क्षेत्र जिसे दोनों पक्षों ने अलग-अलग बदला है, एक
मर्ज कॉन्फ़्लिक्ट (merge conflict) के रूप में बताया जाता है — Git में परिचित
<<<<<<< / ======= /
>>>>>>> निशान। यह पहचानना कि कॉन्फ़्लिक्ट बस "दोनों पक्षों ने पूर्वज के
सापेक्ष एक ही पंक्तियाँ संपादित कीं" है, इन्हें सुलझाना कहीं कम रहस्यमय बना देता है।
व्यवहार में डिफ़िंग कहाँ इस्तेमाल होती है
- कोड समीक्षा। हर पुल रिक्वेस्ट एक डिफ़ है; समीक्षक पूरी फ़ाइलें दोबारा पढ़ने के बजाय जोड़ी और हटाई गई पंक्तियाँ पढ़ते हैं।
- दस्तावेज़ और कॉन्ट्रैक्ट तुलना। किसी समझौते, नीति या लेख के दो संशोधनों की तुलना करके ठीक-ठीक देखना कि कौन-से शब्द बदले।
- कॉन्फ़िगरेशन ड्रिफ़्ट। किसी सर्वर के लाइव कॉन्फ़िगरेशन की ज्ञात-सही संस्करण से डिफ़ लेकर अनचाहे बदलाव खोजना।
- लॉग और डेटा विश्लेषण। दो एक्सपोर्ट, दो CSV स्नैपशॉट या दो लॉग फ़ाइलों की तुलना करके ठीक-ठीक अलग करना कि रन के बीच क्या बदला।
- साहित्यिक-चोरी और समानता पहचान। साझा उपअनुक्रम का आकार इस बात का सीधा माप है कि दो दस्तावेज़ कितने एक जैसे हैं।
हर मामले में सवाल वही है जिसका जवाब Hunt, McIlroy और Myers ने दिया: वह सबसे छोटा, सबसे स्पष्ट बदलावों का समूह क्या है जो पहले टेक्स्ट को दूसरे में बदल देता है?
पंक्ति-डिफ़ की सीमाएँ: खिसके हुए ब्लॉक और गड़बड़ आउटपुट
एक पंक्ति-डिफ़ एक ठीक-ठीक सवाल का जवाब देती है — दो फ़ाइलों के बीच पंक्ति जोड़ों और हटावों का सबसे छोटा अनुक्रम — और उस दृष्टिकोण के कुछ वास्तविक अंधे धब्बे हैं जिन्हें समझना उपयोगी है। सबसे आम है खिसका हुआ कोड (moved code): यदि आप किसी फ़ाइल के ऊपर से एक फ़ंक्शन काटकर उसे नीचे चिपका देते हैं, तो एक सामान्य डिफ़ के पास "खिसका हुआ" जैसी कोई अवधारणा नहीं है। यह ब्लॉक को एक जगह हटाया गया और दूसरी जगह जोड़ा गया बताती है, जिससे बदलाव का प्रत्यक्ष आकार दोगुना दिखता है। कुछ टूल डिफ़ के ऊपर एक अलग पास के रूप में मूव-पहचान जोड़ते हैं; अंतर्निहित LCS परिणाम फिर भी दोनों प्रतियों को स्वतंत्र रूप से मानता है।
दूसरी सीमा है संरेखण अस्पष्टता (alignment ambiguity)। जब किसी फ़ाइल में कई एक-सी पंक्तियाँ होती हैं — खाली पंक्तियाँ, बंद ब्रेसेस, दोहराया गया बॉयलरप्लेट — तो कई अलग-अलग डिफ़ समान रूप से न्यूनतम हो सकती हैं, और एक लालची Myers खोज ऐसी चुन सकती है जो ग़लत ब्रेस को ग़लत ब्रेस के साथ जोड़ दे, जिससे एक सुथरा संपादन बिखरा हुआ दिखता है। यह ठीक वही समस्या है जिसे patience और histogram एल्गोरिथ्म पहले अद्वितीय पंक्तियों पर टिककर निशाना बनाते हैं। तीसरी यह कि पंक्ति-डिफ़ पंक्ति के भीतर के बदलाव के प्रति अंधी है: किसी लंबी पंक्ति में एक वर्ण संपादित करें और पूरी पंक्ति चिह्नित हो जाती है, यही वजह है कि गद्य की तुलना को अक्सर एक शब्द-स्तर पास से लाभ होता है।
इनमें से कोई भी पंक्ति-डिफ़ को ग़लत नहीं बनाता — यह कोड और कॉन्फ़िगरेशन के लिए सही डिफ़ॉल्ट है — पर इसकी मान्यताओं को जानना समझाता है कि दो टूल एक ही "बदलाव" को अलग-अलग क्यों दिखा सकते हैं, और एल्गोरिथ्म या सूक्ष्मता बदलना कभी-कभी एक भ्रामक डिफ़ को स्पष्ट क्यों बना देता है।
अक्सर पूछे जाने वाले सवाल
- यह कौन-सा एल्गोरिथ्म इस्तेमाल करता है?
- LCS (सबसे लंबा साझा उपअनुक्रम — Longest Common Subsequence) एल्गोरिथ्म का उपयोग करते हुए पंक्ति-दर-पंक्ति डिफ़ — वही तरीक़ा जो Unix पर diff(1), git diff और अधिकांश कोड-समीक्षा टूल इस्तेमाल करते हैं। दोनों टेक्स्ट में मौजूद पंक्तियाँ अपरिवर्तित दिखाई जाती हैं; केवल मूल में मौजूद पंक्तियाँ हटाई गई (लाल) चिह्नित होती हैं; केवल संशोधित टेक्स्ट में मौजूद पंक्तियाँ जोड़ी गई (हरा) चिह्नित होती हैं।
- क्या मेरा टेक्स्ट किसी सर्वर पर अपलोड होता है?
- नहीं। डिफ़ पूरी तरह आपके ब्राउज़र में JavaScript से चलती है। आपका टेक्स्ट कभी आपके डिवाइस से बाहर नहीं जाता और पेज लोड होने के बाद ऑफ़लाइन भी काम करता है।
- क्या मैं कोड की डिफ़ ले सकता हूँ?
- हाँ। यह टूल किसी भी सादे टेक्स्ट की डिफ़ लेता है, जिसमें सोर्स कोड, कॉन्फ़िगरेशन फ़ाइलें, JSON, CSV, गद्य और मार्कडाउन शामिल हैं। यह सिंटैक्स हाइलाइटिंग नहीं लगाता, पर पंक्ति-स्तर की डिफ़ इंडेंटेशन और विराम-चिह्नों को सही ढंग से संभालती है।
- मैं कितना बड़ा टेक्स्ट तुलना कर सकता हूँ?
- LCS एल्गोरिथ्म की समय-जटिलता O(n×m) है, इसलिए बहुत बड़ी फ़ाइलें (हज़ारों पंक्तियाँ) धीमी हो सकती हैं। कुछ सौ पंक्तियों तक के दस्तावेज़ों के लिए डिफ़ तुरंत होती है। बड़ी फ़ाइलों के लिए किसी डेस्कटॉप डिफ़ टूल (diff, vimdiff, VS Code compare) का उपयोग करने पर विचार करें।
- "खाली जगह अनदेखा करें" क्या करता है?
- टिक करने पर, तुलना से पहले हर पंक्ति को ट्रिम कर दिया जाता है, इसलिए केवल आगे-पीछे की खाली जगह वाले बदलाव की पंक्ति अपरिवर्तित मानी जाती है। यह Unix पर diff -b के बराबर है — उस कोड की तुलना के लिए उपयोगी जिसे फिर से फ़ॉर्मैट या फिर से इंडेंट किया गया हो।