diff در گیت چیست و چه‌وقت استفاده می‌شود؟
آموزش

diff در گیت چیست و چه‌وقت استفاده می‌شود؟

git diff دو نسخه از کدها را کنار هم می‌گذارد و خط‌به‌خط نشان می‌دهد چه چیزی عوض شده. یک نگاه به diff قبل از هر add و commit کافی است

تحریریه‌ی دینا کد
نویسنده
۷ دقیقه مطالعه۳ بازدید
اشتراک گذاری۳
فهرست مطالب
  1. ۱خروجی git diff را چطور بخوانم؟
  2. ۲خط @@ در diff یعنی چه؟
  3. ۳git diff بدون آرگومان کدام دو نسخه را مقایسه می‌کند؟
  4. ۴چرا git diff برای فایل جدید چیزی نشان نمی‌دهد؟
  5. ۵تفاوت دو کامیت یا دو شاخه را چطور ببینم؟
  6. ۶از کجا تمرینش کنم؟

diff یعنی تفاوت. در گیت، دستور git diff دو نسخه از فایل‌های پروژه را کنار هم می‌گذارد و خط‌به‌خط نشان می‌دهد چه اضافه شده، چه حذف شده و کدام خط عوض شده است. جای درستش قبل از هر add و commit است؛ یک نگاه به diff و می‌فهمی دقیقا داری چه چیزی را ثبت می‌کنی.

خروجی git diff را چطور بخوانم؟

در مخزن آزمایشی که برای همین مطلب ساختم، یک تابع جمع را عمدا به ضرب تغییر دادم و خروجی واقعی گیت این بود:

bash$ git diff -- calc.py
diff --git a/calc.py b/calc.py
index 2f6d2d2..ee74643 100644
--- a/calc.py
+++ b/calc.py
@@ -1,6 +1,6 @@
 def add(a, b):
     """sums two numbers"""
-    return a + b
+    return a * b
 
 def main():
     print(add(2, 3))

دو خط --- a/calc.py و +++ b/calc.py دو نسخه‌ی درگیر را معرفی می‌کنند؛ a قدیمی است و b جدید. خط index هش داخلی خود گیت است و کاری با آن نداری. خط‌های بی‌علامت تغییر نکرده‌اند و فقط نشان می‌دهند تغییرها کجای فایل افتاده. خط با minus در نسخه‌ی قدیمی بوده و دیگر نیست، خط با plus در نسخه‌ی جدید است. یک خط عوض‌شده همیشه جفت می‌آید، یک minus و یک plus، چون گیت فقط حذف و اضافه می‌کند. دو خط تیره بعد از diff هم یعنی فقط همین فایل را بگیر.

خط @@ در diff یعنی چه؟

گیت کل فایل را یک‌جا نشان نمی‌دهد و فقط حوالی هر تغییر را می‌آورد. سر هر تکه یک خط @@ ایستاده که آدرسش را می‌گوید. در مثال بالا تکه‌ی قدیمی از خط ۱ شروع می‌شود و ۶ خط می‌گیرد؛ تکه‌ی جدید هم از خط ۱ با همان ۶ خط. عدد اول خط شروع است و عدد دوم تعداد خط‌های تکه را نشان می‌دهد، با احتساب خط‌های بدون تغییرِ اطراف.

گاهی بعد از @@ اسم یک تابع دیده می‌شود، مثل @@ -5,3 +5,4 @@ def add(a, b):؛ گیت نزدیک‌ترین تعریف تابع به آن تکه را پیدا کرده تا بفهمی تغییر داخل کدام تابع افتاده. اگر فقط فاصله‌های یک خط عوض شده باشند و diff پر از minus و plus شود، فلگ -w فاصله‌ها را نادیده می‌گیرد؛ در آزمایش من با -w خروجی کاملا خالی برمی‌گشت.

git diff بدون آرگومان کدام دو نسخه را مقایسه می‌کند؟

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

bash$ git diff            # دیسک در برابر stage
$ git diff --staged   # stage در برابر آخرین کامیت
$ git diff HEAD       # دیسک در برابر آخرین کامیت؛ جمع همه‌ی تغییرها

حالا ببین هر دستور کدام سهم را نشان می‌دهد. یک فایل را add کردم و دیگری را نه، بعد سه دستور را پشت‌سرهم زدم:

bash$ git diff --stat
 calc.py  | 1 +
 1 file changed, 1 insertion(+)

$ git diff --staged --stat
 note.txt | 1 +
 1 file changed, 1 insertion(+)

$ git diff HEAD --stat
 calc.py  | 1 +
 note.txt | 1 +
 2 files changed, 2 insertions(+)

هر دستور فقط سهم خودش را نشان داد. --staged همان --cached قدیمی است و هر دو یک کار می‌کنند.

چرا git diff برای فایل جدید چیزی نشان نمی‌دهد؟

دو دلیل رایج دارد. اول اینکه همه‌چیز از قبل add شده و خروجی خالی یعنی هیچ چیز stage نشده نمانده؛ سراغ git diff --staged برو. دوم و مهم‌تر، فایل untracked است؛ گیت تا فایل تازه را add نکنی اصلا نمی‌داند وجود دارد و diff از کنارش رد می‌شود:

bash$ echo "console.log('x')" > app.js
$ git diff -- app.js

$ git add app.js
$ git diff --staged --stat
 app.js | 1 +
 1 file changed, 1 insertion(+)

بعد از ساخت فایل، git diff هیچ حرفی نزد؛ فقط بعد از add فایل وارد بازی شد. اگر diff ساکت است و انتظار تغییری داری، اول git status را بزن تا ببینی گیت چه چیزی دیده است.

تفاوت دو کامیت یا دو شاخه را چطور ببینم؟

دو شناسه به دستورش بده؛ هش کامیت، تگ یا اسم شاخه:

bash$ git diff v1 v2 --stat
 calc.py | 2 ++
 1 file changed, 2 insertions(+)

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

bash$ git diff main..feature --stat
 feat.txt | 1 +
 main.txt | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

$ git diff main...feature --stat
 feat.txt | 1 +
 1 file changed, 1 insertion(+)

نسخه‌ی دو نقطه‌ای ادعا می‌کند main.txt حذف شده، در حالی که کسی آن را حذف نکرده؛ فایل فقط از نوک feature غایب است چون main بعد از جداشدن آن را ساخته است. وقتی می‌پرسی «این شاخه چه آورده؟» جواب همان سه نقطه است. اگر با خود شاخه‌ها راحت نیستی، مطلب branch در گیت چیست از صفر می‌گوید.

از کجا تمرینش کنم؟

یک عادت ساده بساز: قبل از هر add یک git diff بزن و قبل از هر commit یک git diff --staged. در دوره‌ی آموزش گیت و گیت‌هاب همین تمرین‌ها را در مرورگر انجام می‌دهی؛ دستور را می‌زنی و خروجی واقعی را همان‌جا می‌بینی، بدون نصب چیزی. و اگر روزی در diff دیدی اشتباه تغییر داده‌ای، مطلب تفاوت git reset و git revert می‌گوید چطور تمیز به عقب برگردی.

برچسب‌ها:#گیت#مبتدی
تحریریه‌ی دینا کد
نویسنده

نظرها

اولین نفری باشید که نظر می‌دهد
برای ثبت نظر وارد شوید

سؤالتان را بپرسید یا تجربه‌تان را بنویسید تا نویسنده‌ی مطلب پاسخ بدهد.

مطالب مرتبط

از همین دسته
branch در گیت چیست و چه‌وقت بسازیمش؟
آموزش

branch در گیت چیست و چه‌وقت بسازیمش؟

branch یک اشاره‌گر متحرک به یکی از کامیت‌هاست، نه کپی پوشه‌ی پروژه. با git switch می‌سازی و جابه‌جا می‌شوی، با merge برمی‌گردانی و در پایان پاکش می‌کنی

تحریریه‌ی دینا کد۷ دقیقه
اینترفیس در TypeScript چیست و چه‌وقت به کار می‌آید؟
آموزش

اینترفیس در TypeScript چیست و چه‌وقت به کار می‌آید؟

اینترفیس قراردادِ شکلِ داده در TypeScript است؛ هر شیء‌ای که با آن جا می‌گیرد باید فیلدهای لازم را داشته باشد، وگرنه برنامه اصلاً کامپایل نمی‌شود

تحریریه‌ی دینا کد۴ دقیقه
۲۰ تمرین پایتون با جواب، از آسان به سخت
آموزش

۲۰ تمرین پایتون با جواب، از آسان به سخت

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

تحریریه‌ی دینا کد۸ دقیقه