فهرست مطالب
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 میگوید چطور تمیز به عقب برگردی.





نظرها
اولین نفری باشید که نظر میدهدسؤالتان را بپرسید یا تجربهتان را بنویسید تا نویسندهی مطلب پاسخ بدهد.