🧑‍💻 Git & Diff Tools

مقایسه فایل‌ها با diff

چگونه تفاوت بین دو فایل را سریع و دقیق پیدا کنیم؟ با ابزار خط فرمان diff و هم‌تایان آن آشنا شوید.

📂 diff • patch • wdiff • meld • KDiff3

🗺️ مسیر یادگیری مقایسه فایل‌ها

  1. 👀 مقایسه دستی – پرخطا و وقت‌گیر
  2. 🛠️ معرفی ابزار diff
  3. 📄 مثال اول – دو نسخه از یک تابع
  4. 🔍 تفسیر خروجی diff
  5. 📄 مثال دوم – تغییرات بیشتر
  6. 🧩 درک بخش‌های diff (c و a)
  7. 📋 خروجی یکپارچه با flag -u
  8. 🎨 ابزارهای دیگر – wdiff و ابزارهای گرافیکی

👀 گام ۱: مقایسه دستی – پرخطا و وقت‌گیر

فرض کن دو نسخه از یک کد داری و می‌خواهی ببینی چه تفاوتی بین آن‌ها وجود دارد. یک راه این است که هر دو فایل را کنار هم در ویرایشگر باز کنی و خط‌به‌خط آن‌ها را با چشم مقایسه کنی.

اما این روش بسیار خطاپذیر است. مغز انسان برای تشخیص تغییرات جزئی در میان حجم زیادی از متن، چندان قابل اعتماد نیست. به‌راحتی ممکن است یک تفاوت کوچک را نادیده بگیری و بعداً دچار سردرگمی شوی.

💡 هرچه فایل بزرگ‌تر باشد، مقایسه دستی دشوارتر و اشتباه‌آمیزتر می‌شود. خوشبختانه ابزارهای خودکاری وجود دارند که این کار را برای ما انجام می‌دهند.

🛠️ گام ۲: معرفی ابزار diff

ابزار خط فرمان diff یک راه حل عالی برای مقایسه خودکار فایل‌هاست. این دستور می‌تواند تفاوت بین دو فایل یا حتی دو پوشه را در قالب‌های مختلف نمایش دهد.

با استفاده از diff، دیگر نیازی نیست خودت به دنبال تغییرات بگردی؛ ابزار دقیقاً به تو نشان می‌دهد کدام خط‌ها تغییر کرده‌اند، کدام خط‌ها حذف شده‌اند و کدام خط‌ها اضافه شده‌اند.

⚡ diff یکی از پرکاربردترین دستورات در خط فرمان است و در کنار Git و سایر سیستم‌های کنترل نسخه، به شما کمک می‌کند تغییرات را بهتر درک کنید.

📄 گام ۳: مثال اول – دو نسخه از یک تابع

بیایید با یک مثال ساده شروع کنیم. دو فایل به نام‌های rearrange1.py و rearrange2.py داریم که هر دو شامل یک تابع برای مرتب‌سازی نام هستند. ابتدا با دستور cat محتوای آن‌ها را می‌بینیم:

$ cat rearrange1.py
#!/usr/bin/env python3

import re

def rearrange_name(name):
    result = re.search(r"^([\w .]*), ([\w .]*)$", name)
    if result == None:
        return name
    return "{} {}".format(result[2], result[1])
$ cat rearrange2.py
#!/usr/bin/env python3

import re

def rearrange_name(name):
    result = re.search(r"^([\w .-]*), ([\w .-]*)$", name)
    if result == None:
        return name
    return "{} {}".format(result[2], result[1])

اگر دقت کنی، تفاوت در الگوی عبارت منظم (regex) است. در فایل اول از [\w .]* استفاده شده، اما در فایل دوم [\w .-]* به کار رفته که کاراکتر - را هم پوشش می‌دهد. شاید با چشم این تفاوت را ببینی، اما در فایل‌های بزرگ‌تر پیدا کردن چنین تغییراتی سخت است.

🔍 گام ۴: تفسیر خروجی diff

حالا بیاییم از دستور diff استفاده کنیم تا تفاوت را دقیقاً ببینیم:

$ diff rearrange1.py rearrange2.py
6c6
<     result = re.search(r"^([\w .]*), ([\w .]*)$", name)
---
>     result = re.search(r"^([\w .-]*), ([\w .-]*)$", name)

خروجی فقط خط‌های متفاوت را نشان می‌دهد. در اینجا:

بنابراین می‌فهمیم که دقیقاً یک خط با خط دیگری جایگزین شده است. این نوع تغییر در کدنویسی بسیار رایج است.

📌 خروجی diff به‌خوبی نشان می‌دهد که کدام بخش‌ها دستخوش تغییر شده‌اند و به ما کمک می‌کند تا بدون سردرگمی، تفاوت‌ها را درک کنیم.

📄 گام ۵: مثال دوم – تغییرات بیشتر

بیایید مثال دیگری را بررسی کنیم. دو فایل validations1.py و validations2.py که در آن تغییرات بیشتری وجود دارد.

$ diff validations1.py validations2.py
5c5,6
<     assert (type(username) == str), "username must be a string"
---
>     if type(username) != str:
>         raise TypeError("username must be a string")
11a13,15
>         return False
>     # Usernames can't begin with a number
>     if username[0].isnumeric():

خروجی این بار دو بخش دارد:

اما ممکن است بخش دوم کمی گنگ به نظر برسد؛ چرا که فقط خطوط اضافه شده را نشان می‌دهد، بدون اینکه بگوید کدام شرط مربوط به کدام بخش کد است.

🧩 گام ۶: درک بخش‌های diff (c و a)

در خروجی diff، بخش‌ها با شماره خطوط و یک حرف مشخص می‌شوند:

در مثال دوم، بخش 11a13,15 به این معناست که بعد از خط ۱۱ فایل اول، خطوط ۱۳ تا ۱۵ فایل دوم اضافه شده‌اند. اما چون زمینه (context) کافی نداریم، ممکن است متوجه نشویم که این اضافه‌ها مربوط به چه بخشی از کد هستند.

💡 برای درک بهتر تغییرات، می‌توانیم از حالت یکپارچه (unified) استفاده کنیم که زمینه بیشتری را نشان می‌دهد.

📋 گام ۷: خروجی یکپارچه با flag -u

با اضافه کردن پرچم -u به دستور diff، خروجی را به صورت یکپارچه (unified) دریافت می‌کنیم:

$ diff -u validations1.py validations2.py
--- validations1.py	2019-06-06 14:28:49.639209499 +0200
+++ validations2.py	2019-06-06 14:30:48.019360890 +0200
@@ -2,7 +2,8 @@
 

 def validate_user(username, minlen):
-    assert type(username) == str, "username must be a string"
+    if type(username) != str:
+        raise TypeError("username must be a string")
     if minlen < 1:
         raise ValueError("minlen must be at least 1")
     
@@ -10,5 +11,8 @@
         return False
     if not username.isalnum():
         return False
+    # Usernames can't begin with a number
+    if username[0].isnumeric():
+        return False
     return True

در این حالت:

این خروجی بسیار واضح‌تر است و به راحتی می‌توان دید که در فایل دوم، یک بلاک if جدید اضافه شده که بخشی از زنجیره شرط‌هاست.

⚠️ حالت unified به ویژه وقتی تغییرات زیاد هستند یا به هم مرتبط‌اند، بسیار مفید است و در ابزارهایی مثل Git نیز از همین قالب استفاده می‌شود.

🎨 گام ۸: ابزارهای دیگر – wdiff و ابزارهای گرافیکی

diff محبوب‌ترین ابزار مقایسه فایل‌هاست، اما تنها گزینه نیست. برخی ابزارها تفاوت‌ها را به شکل دیگری نمایش می‌دهند:

این ابزارها با ارائه زمینه بصری بیشتر، درک تغییرات را آسان‌تر می‌کنند، مخصوصاً وقتی با کدهای بزرگ یا چندین نسخه مواجه هستیم.

🚀 جمع‌بندی: diff و ابزارهای مشابه به ما کمک می‌کنند تا تغییرات بین فایل‌ها را سریع، دقیق و بدون خطای انسانی تشخیص دهیم. در جلسه‌ی بعدی خواهیم دید که چگونه از این تفاوت‌ها برای اعمال تغییرات (patch) استفاده کنیم.

🎉 با اتمام این جلسه، شما با ابزار قدرتمند diff برای مقایسه‌ی فایل‌ها آشنا شدید و آماده‌ی یادگیری patch هستید.

🌐 با ما همراه باشید

برای دریافت آخرین مطالب، پرسش و پاسخ و اطلاع از رویدادهای مرتبط، ما را در شبکه‌های اجتماعی دنبال کنید.