🧑‍💻 Git & Diff Tools

کاربرد عملی diff و patch

با کمک یک سناریوی واقعی، یاد می‌گیریم چطور باگ‌های یک اسکریپت را پیدا کنیم، آن را تعمیر کنیم و تغییرات را با diff و patch به همکار خود منتقل کنیم.

📂 diff • patch • sys.exit • disk usage • bug fixing

🗺️ مسیر یادگیری کاربرد عملی diff و patch

  1. 🤝 سناریو: کمک به همکار برای رفع باگ
  2. 📂 کپی گرفتن از فایل اصلی
  3. 🐛 باگ اول: return خارج از تابع
  4. 🔧 رفع باگ اول با sys.exit
  5. 🐛 باگ دوم: تبدیل دوبرابر به گیگابایت
  6. 🔧 رفع باگ دوم با اصلاح پارامتر
  7. ✅ تست نهایی و موفقیت
  8. 📄 تولید فایل diff و ارسال به همکار
  9. 🔄 اعمال patch روی فایل اصلی
  10. 📝 جمع‌بندی و قدم بعدی

🤝 گام ۱: سناریو – کمک به همکار برای رفع باگ

تصور کن یک همکار از تو کمک خواسته تا یک اسکریپت به نام disk_usage.py را تعمیر کنی. هدف این اسکریپت این است که بررسی کند چقدر فضای دیسک در حال استفاده است و اگر فضای خالی برای عملکرد عادی خیلی کم بود، یک پیام خطا چاپ کند.

اما اسکریپت در حال حاضر شکست خورده (broken) است و چند باگ دارد. در این درسنامه، با هم این باگ‌ها را پیدا و رفع می‌کنیم و از ابزارهای diff و patch استفاده می‌کنیم تا تغییرات را به همکار خود منتقل کنیم.

💡 نکته: این یک مثال واقعی از همکاری تیمی در توسعه نرم‌افزار است. با استفاده از diff و patch، می‌توانیم تغییرات را دقیقاً به دیگران منتقل کنیم و از اشتباهات جلوگیری کنیم.

📂 گام ۲: کپی گرفتن از فایل اصلی

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

برای این کار، از دستور cp (کپی) استفاده می‌کنیم و دو کپی می‌سازیم:

$ cp disk_usage.py disk_usage_original.py
$ cp disk_usage.py disk_usage_fixed.py

⚡ ترفند: همیشه قبل از شروع تغییرات، از فایل اصلی کپی بگیر. این کار به تو امکان می‌دهد هر زمان خواستی، تغییرات را با نسخه اصلی مقایسه کنی و اگر کار به خطا رفت، برگردی به نقطه اول.

🐛 گام ۳: باگ اول – return خارج از تابع

بیایید ابتدا اسکریپت را اجرا کنیم تا ببینیم چه خطایی می‌دهد:

$ ./disk_usage_fixed.py
File "./disk_usage_fixed.py", line 19
    return 1
    ^
SyntaxError: 'return' outside function

مترجم پایتون (Python interpreter) از این خطا ناراضی است! خطا می‌گوید: «return خارج از تابع». یعنی در جای‌ای از کد، از دستور return استفاده شده که داخل هیچ تابعی قرار ندارد.

همان‌طور که ممکن است به خاطر داشته باشی، در پایتون ما فقط می‌توانیم از دستور return در داخل توابع استفاده کنیم. وقتی کد را بررسی می‌کنیم، می‌بینیم که یک return 1 و return 0 در بخش اصلی اسکریپت (خارج از هر تابعی) قرار گرفته‌اند که این اشتباه است.

⚠️ هشدار: استفاده از return خارج از تابع، یک خطای نحوی (syntax error) محسوب می‌شود و مانع از اجرای اسکریپت می‌شود.

🔧 گام ۴: رفع باگ اول با sys.exit

برای رفع این مشکل، دو راه داریم:

در اینجا راه دوم را انتخاب می‌کنیم. برای این کار:

توضیح: sys.exit() یک تابع است که باعث پایان یافتن برنامه با یک کد خروج (exit code) مشخص می‌شود. عدد 0 معمولاً به معنای «موفقیت» و عدد 1 به معنای «خطا» است.

📌 نکته: استفاده از sys.exit یک روش استاندارد در پایتون برای پایان دادن به برنامه با کد خروج مناسب است.

کد اصلاح‌شده به این شکل خواهد بود:

#!/usr/bin/env python3

import shutil
import sys      # ← ماژول sys اضافه شد

def check_disk_usage(disk, min_absolute, min_percent):
    """Returns True if there is enough free disk space, false otherwise."""
    du = shutil.disk_usage(disk)
    percent_free = 100 * du.free / du.total
    # ... (بقیه کد)

if not check_disk_usage("/", 2*2**30, 10):
    print("ERROR: Not enough disk space")
    sys.exit(1)   # ← به جای return 1

print("Everything ok")
sys.exit(0)      # ← به جای return 0

🐛 گام ۵: باگ دوم – تبدیل دوبرابر به گیگابایت

حالا بیایید نسخه جدید اسکریپت را اجرا کنیم تا ببینیم چه می‌شود:

$ ./disk_usage_fixed.py
ERROR: Not enough disk space

خطای نحوی (syntax error) را رفع کردیم، اما الان اسکریپت می‌گوید «فضای کافی روی دیسک وجود ندارد». در حالی که ما می‌دانیم فضای خالی داریم! مشکل از کجاست؟

اگر با دقت به کد نگاه کنیم، متوجه می‌شویم که اسکریپت دو بار به گیگابایت تبدیل انجام می‌دهد:

یعنی تبدیل به گیگابایت دو بار انجام شده است. یک بار در هنگام فراخوانی تابع و یک بار در داخل تابع. این باعث می‌شود که عدد مورد انتظار ۲ گیگابایت، در عمل خیلی بزرگ‌تر شود و اسکریپت فکر کند فضای کافی وجود ندارد.

💡 نکته: در پایتون، 2**30 برابر با ۱,۰۷۳,۷۴۱,۸۲۴ بایت (یک گیگابایت) است. پس 2*2**30 یعنی ۲,۱۴۷,۴۸۳,۶۴۸ بایت یا همان ۲ گیگابایت.

🔧 گام ۶: رفع باگ دوم با اصلاح پارامتر

برای رفع این مشکل، باید یکی از تبدیل‌ها را حذف کنیم. بهترین کار این است که نحوه صدا زدن تابع را تغییر دهیم و پارامتر را به شکل درست اصلاح کنیم.

با توجه به اینکه تابع check_disk_usage انتظار مقدار گیگابایت را دارد (چون داخل تابع، دوباره به گیگابایت تبدیل می‌کند)، باید عدد 2 را به عنوان پارامتر بدهیم و تبدیل به بایت را حذف کنیم.

پس خط فراخوانی تابع را از:

if not check_disk_usage("/", 2*2**30, 10):

به این شکل تغییر می‌دهیم:

if not check_disk_usage("/", 2, 10):

حالا تابع با پارامتر ۲ گیگابایت صدا زده می‌شود و داخل تابع، این عدد را به درستی با فضای آزاد مقایسه می‌کند.

🧠 نکته: دقت کن که تابع check_disk_usage سه پارامتر دارد: disk (مسیر دیسک)، min_absolute (حداقل فضای آزاد بر حسب گیگابایت)، و min_percent (حداقل درصد فضای آزاد).

✅ گام ۷: تست نهایی و موفقیت

حالا بیایید دوباره اسکریپت را اجرا کنیم و ببینیم نتیجه چیست:

$ ./disk_usage_fixed.py
Everything ok

عالی! اسکریپت به درستی کار می‌کند و پیام «همه چیز خوب است» را چاپ می‌کند. یعنی فضای دیسک کافی است و باگ‌ها را به‌درستی رفع کرده‌ایم.

✅ نتیجه: با رفع دو باگ – یکی مربوط به استفاده نادرست از return و دیگری مربوط به تبدیل دوبرابر به گیگابایت – اسکریپت به‌درستی اجرا شد.

📄 گام ۸: تولید فایل diff و ارسال به همکار

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

نسخه اصلی (که دست نخورده باقی مانده) را با نسخه تعمیرشده مقایسه می‌کنیم:

$ diff -u disk_usage_original.py disk_usage_fixed.py > disk_usage.diff

حالا بیایید محتوای فایل diff را با cat ببینیم:

$ cat disk_usage.diff
--- disk_usage_original.py	2019-06-22 15:13:38.591579963 -0700
+++ disk_usage_fixed.py	2019-06-22 15:41:35.013023839 -0700
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3

 import shutil
+import sys

 def check_disk_usage(disk, min_absolute, min_percent):
     """Returns True if there is enough free disk space, false otherwise."""
@@ -14,9 +15,9 @@
     return True

 # Check for at least 2 GB and 10% free
-if not check_disk_usage("/", 2*2**30, 10):
+if not check_disk_usage("/", 2, 10):
     print("ERROR: Not enough disk space")
-    return 1
+    sys.exit(1)

 print("Everything ok")
-return 0
+sys.exit(0)

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

📌 نکته: فایل diff دقیقاً نشان می‌دهد که چه چیزی تغییر کرده است: اضافه شدن import sys، اصلاح پارامتر فراخوانی تابع، و جایگزینی return با sys.exit.

🔄 گام ۹: اعمال patch روی فایل اصلی

حالا فرض کن همکار ما فایل diff را دریافت کرده است. چگونه باید آن را روی اسکریپت خودش اعمال کند؟ بسیار ساده! با دستور patch:

$ patch disk_usage.py < disk_usage.diff
patching file disk_usage.py

با اجرای این دستور، patch تغییرات را روی فایل disk_usage.py اعمال می‌کند. حالا بیایید بررسی کنیم که اسکریپت به‌درستی اجرا می‌شود:

$ ./disk_usage.py
Everything ok

موفقیت! اسکریپت همکار ما نیز با موفقیت تعمیر شد و پیام «همه چیز خوب است» را چاپ کرد.

✅ نتیجه: با استفاده از patch، تغییرات را به‌سرعت و بدون خطا روی فایل اصلی اعمال کردیم.

📝 گام ۱۰: جمع‌بندی و قدم بعدی

در این درسنامه، با یک سناریوی واقعی آشنا شدیم و دیدیم که چگونه می‌توان با استفاده از ابزارهای diff و patch:

این فرایند هنوز تا حد زیادی دستی است، اما درک این مفاهیم پایه، ما را برای ورود به دنیای سیستم‌های کنترل نسخه (Version Control Systems) مانند Git آماده می‌کند.

در ادامه، یک خلاصه از دستوراتی که یاد گرفتیم در اختیار تو قرار می‌گیرد. حتماً آن را مطالعه کن و سپس به سراغ آزمون تمرینی (practice quiz) برو تا مطمئن شوی همه مفاهیم را به‌خوبی درک کرده‌ای.

🚀 قدم بعدی: در درسنامه‌های آینده، با Git آشنا خواهیم شد که این فرایند را بسیار ساده‌تر و خودکارتر می‌کند.

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

🚀 رفتن به جلسهٔ بعدی 🚀

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

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