بسیاری از فرآیندهای جالب در Git مانند ادغام، rebasing یا حتی committing بر اساس تفاوت ها و وصله ها هستند.

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

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

با این حال، درک تعهدات فردی با نگاه کردن به کل درخت کار واقعاً دشوار است. در عوض، نگاه کردن به تفاوت یک commit با commit والدش مفیدتر است، یعنی تفاوت بین این تعهدات

بنابراین، وقتی می گویم منظورم چیست؟ diff? بیایید با کمی تاریخ شروع کنیم.

تاریخچه Git Diff 📖

Git’s diff بر اساس ابزار تفاوت در سیستم های یونیکس است. diff در اوایل دهه 1970 بر روی سیستم عامل یونیکس توسعه یافت. اولین نسخه منتشر شده با نسخه پنجم یونیکس در سال 1974 عرضه شد.

git diff دستوری است که دو ورودی می گیرد و تفاوت بین آنها را محاسبه می کند. ورودی‌ها می‌توانند commit باشند، اما همچنین فایل‌هایی و حتی فایل‌هایی که هرگز به مخزن معرفی نشده‌اند.

تصویر-214
Git diff دو ورودی می گیرد که می تواند commit یا فایل باشد (منبع: مختصر)

این مهم است – git diff تفاوت بین دو رشته را محاسبه می کند، که اغلب اوقات از کد تشکیل شده است، اما نه لزوما.

زمان دست به دست شدن است 🙌🏻

به شما توصیه می شود هنگام خواندن این پست، دستورات را خودتان اجرا کنید.

این فایل متنی بسیار کوتاه را در نظر بگیرید که نام دارد file.txt در دستگاه من که از 6 خط تشکیل شده است:

تصویر-158
file.txt شامل 6 خط (منبع: مختصر)

حالا این فایل را کمی تغییر دهید. خط دوم را بردارید و یک خط جدید به عنوان خط چهارم وارد کنید. یک را اضافه کنید ! تا انتهای خط آخر، به این نتیجه می رسید:

تصویر-159
پس از اصلاح file.txt، 6 خط مختلف دریافت می کنیم (منبع: مختصر)

این فایل را با یک نام جدید ذخیره کنید، بگویید new_file.txt.

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

git diff -–no-index file.txt new_file.txt

(من توضیح خواهم داد --no-index بعداً این دستور را تغییر دهید.)

تصویر-160
خروجی از git diff (منبع: مختصر)

بنابراین خروجی از git diff خیلی چیزها را نشان می دهد

در حال حاضر، روی قسمتی که با آن شروع می شود تمرکز کنید This is a simple line. می توانید ببینید که خط اضافه شده (// new test) قبل از a + امضا کردن. خط حذف شده قبل از a است - امضا کردن.

جالب توجه است، توجه داشته باشید که Git یک خط اصلاح شده را به عنوان دنباله ای از دو تغییر مشاهده می کند – یک خط را پاک می کند و به جای آن یک خط جدید اضافه می کند. بنابراین پچ شامل حذف آخرین خط و اضافه کردن یک خط جدید که برابر با آن خط است، با اضافه کردن یک !.

تصویر-165
خطوط اضافه قبل از +، حذف خطوط توسط -، و خطوط اصلاح دنباله ای از حذف ها و اضافات هستند (منبع: مختصر)

شرایط patch و diff اغلب به جای یکدیگر استفاده می شوند، اگرچه حداقل از نظر تاریخی تمایزی وجود دارد.

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

این روزها برنامه Unix diff و git diff، می تواند تکه هایی در انواع مختلف تولید کند.

آ patch یک نمایش فشرده از تفاوت بین دو فایل است. نحوه تبدیل یک فایل به فایل دیگر را توضیح می دهد.

یعنی اگر «دستورالعمل‌های» تولید شده توسط را اعمال کنید git diff بر file.txt – یعنی خط دوم را بردارید، وارد کنید // new text به عنوان خط چهارم، و دیگری اضافه کنید ! تا آخرین خط – شما محتوای آن را دریافت خواهید کرد new_file.txt.

نکته مهم دیگری که باید به آن توجه داشت این است که یک پچ نامتقارن است: پچ از file.txt به new_file.txt همان پچ برای جهت دیگر نیست.

بنابراین، در این مثال، تولید a patch بین new_file.txt و file.txt، در این ترتیب، دقیقاً به معنای دستورالعمل های مخالف قبلی است – به جای حذف خط دوم و غیره را اضافه کنید.

تصویر-167
آ patch شامل دستورالعمل های نامتقارن برای رسیدن از یک فایل به فایل دیگر (منبع: مختصر)

آن را امتحان کنید:
git diff -–no-index new_file.txt file.txt

تصویر-169
در حال دویدن git diff در جهت معکوس دستورالعمل های معکوس را نشان می دهد – به جای حذف یک خط اضافه کنید و غیره (منبع: مختصر)

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

ساختار یک تفاوت 🔍

بنابراین، وقت آن است که عمیق تر شیرجه بزنید.

ایجاد تفاوت از file.txt به new_file.txt دوباره، و خروجی را با دقت بیشتری در نظر بگیرید:

git diff -–no-index file.txt new_file.txt

خط اول فایل های مقایسه شده را معرفی می کند. Git همیشه نام یک فایل را می دهد a، و دیگری نام b. بنابراین در این مورد file.txt نامیده میشود a، در حالیکه new_file.txt نامیده میشود b.

تصویر-170
خط اول در diff خروجی ‘s فایل های در حال مقایسه را معرفی می کند (منبع: مختصر)

سپس خط دوم، با شروع index، شامل blob SHA های این فایل ها می باشد. بنابراین حتی اگر در مورد ما آنها حتی در یک مخزن Git ذخیره نمی شوند، Git مقادیر SHA-1 مربوطه آنها را نشان می دهد.

اگر در مورد blobs به طور خاص و اشیاء Git به طور کلی نیاز به یادآوری دارید، این پست را بررسی کنید.

مقدار سوم در این خط، 100644، “بیت های حالت” است، که نشان می دهد این یک فایل “عادی” است: اجرایی نیست و یک پیوند نمادین نیست.

استفاده از دو نقطه (..) در اینجا بین blob SHAها فقط به عنوان جداکننده است (بر خلاف موارد دیگر که در Git استفاده می شود).

سایر خطوط سرصفحه ممکن است بیت های حالت قدیمی و جدید را در صورت تغییر، نام فایل های قدیمی و جدید را در صورت تغییر نام و غیره نشان دهند.

تصویر-171
خط دوم در diff خروجی شامل blob SHAهای فایل های مقایسه شده و همچنین بیت های حالت است (منبع: مختصر)

اگر این وصله بعداً توسط Git به همان پروژه اعمال شود و در حین اعمال آن تداخل داشته باشد، SHAهای blob (که “شناسه های blob” نیز نامیده می شود) مفید هستند.

بعد از شناسه های blob، دو خط داریم: یکی با شروع - نشانه ها، و دیگری با شروع + نشانه ها این هدر سنتی “تفاوت یکپارچه” است که دوباره فایل های در حال مقایسه و جهت تغییرات را نشان می دهد: - نشانه ها خطوطی را در نسخه A نشان می دهند اما در نسخه B وجود ندارند و + علائم، خطوطی که در نسخه A وجود ندارد اما در B موجود است.

اگر وصله مربوط به این فایل باشد که به طور کامل اضافه یا حذف شده است، یکی از این موارد خواهد بود /dev/null برای نشان دادن آن

تصویر-172
- نشانه ها خطوطی را در نسخه A نشان می دهند اما در نسخه B وجود ندارند. و + علائم، خطوطی که در نسخه A وجود ندارد اما در B موجود است (منبع: مختصر)

موردی را در نظر بگیرید که یک فایل را حذف می کنیم:
rm file.txt

و سپس استفاده می کنیم git diff:

تصویر-173
diffخروجی یک فایل حذف شده (منبع: مختصر)

نسخه A، نشان دهنده وضعیت شاخص، در حال حاضر است file.txt، در مقایسه با dir کاری که در آن این فایل وجود ندارد، بنابراین وجود دارد /dev/null. همه خطوط قبل از - نشانه هایی که فقط در نسخه A وجود دارند.

بازگشت به تفاوت قبلی:

تصویر-174
diffخروجی ‘s شامل بخش های تغییرات به نام “hunks” یا “chunks” است (منبع: مختصر)

پس از این هدر تفاوت یکپارچه، به بخش اصلی تفاوت می‌رسیم که شامل «بخش‌های تفاوت» است که در Git «hunks» یا «chunks» نیز نامیده می‌شود.

توجه داشته باشید که این اصطلاحات به جای یکدیگر استفاده می شوند و ممکن است در مستندات و آموزش های Git و همچنین کد منبع Git به هر یک از آنها برخورد کنید.

پیشنهاد می‌کنیم بخوانید:  بهترین شیوه های Git – راهنمای کنترل نسخه برای مبتدیان

هر قطعه با یک خط شروع می شود که با دو خط شروع می شود @ نشانه ها این نشانه ها حداکثر با چهار عدد دنبال می شوند و سپس یک هدر برای قطعه – که یک حدس آموزشی توسط Git است که گاهی اوقات به خوبی کار می کند.

معمولاً در صورت امکان، شروع یک تابع یا یک کلاس را شامل می شود. در این مثال هیچ چیزی را شامل نمی شود زیرا این یک فایل متنی است، بنابراین مثال دیگری را برای لحظه ای در نظر بگیرید:

git diff -–no-index example.py example_changed.py

تصویر-175
در صورت امکان، Git شامل یک هدر برای هر هانک است، به عنوان مثال یک تابع یا تعریف کلاس (منبع: مختصر)

در تصویر بالا هدر hunk شامل ابتدای تابعی است که شامل خطوط تغییر یافته است – def example_function(x).

سپس به مثال قبلی خود بازگردید:

تصویر-174
بازگشت به قبلی diff (منبع: مختصر)

بعد از آن دو @ نشانه ها، شما می توانید چهار عدد را پیدا کنید.

اعداد اول با a جلو می آیند - همانطور که اشاره می کنند علامت بزنید file A. عدد اول نشان دهنده شماره خط مربوط به سطر اول در است file A این قطعه به در مثال بالا اینطور است 1، به این معنی که خط This is a simple file مربوط به شماره خط است 1 در نسخه file A.

این عدد با کاما (,) و سپس تعداد خطوطی که این تکه از آن تشکیل شده است file A. این عدد شامل تمام خطوط زمینه (خطوط قبل با یک فاصله در تفاوت)، یا خطوط مشخص شده با یک - علامت، همانطور که آنها بخشی از file A، اما نه خطوط مشخص شده با a + را امضا کنید، زیرا آنها در آن وجود ندارند file A.

در مثال بالا این عدد است 6، شمارش خط زمینه This is a simple file، - خط It has a nice poem:، سپس سه خط زمینه و در آخر Are belong to you.

همانطور که می بینید، خطوطی که با یک کاراکتر فاصله شروع می شوند، خطوط زمینه هستند، به این معنی که همانطور که در هر دو نشان داده شده است ظاهر می شوند file A و file B.

سپس، ما یک + برای علامت گذاری دو عددی که به آنها اشاره می شود علامت بزنید file B. ابتدا شماره خط مربوط به سطر اول در file Bو به دنبال آن تعداد خطوطی که این قطعه شامل – in است file B.

این شماره شامل تمام خطوط زمینه و همچنین خطوطی است که با علامت علامت گذاری شده اند + علامت، همانطور که آنها بخشی از file B، اما نه خطوط مشخص شده با a - امضا کردن.

بعد از هدر قطعه، خطوط واقعی را دریافت می کنیم – یا زمینه، - یا + خطوط

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

تصویر-176
فرمت پچ توسط git diff (منبع: مختصر)

نحوه تولید تفاوت ها ⌨️

مثال بالا دقیقاً تفاوت بین دو فایل را نشان می دهد. یک فایل پچ می‌تواند شامل تفاوت‌هایی برای هر تعداد فایل باشد و git diff برای همه فایل های تغییر یافته در مخزن در یک پچ تفاوت ایجاد می کند.

اغلب، شما خروجی را مشاهده خواهید کرد git diff نمایش دو نسخه از یکسان فایل و تفاوت بین آنها

برای نشان دادن، این مخزن دیگر را در نظر بگیرید:

cd ~/brief-example

در وضعیت فعلی، دایرکتوری فعال یک مخزن Git با وضعیت تمیز است:

git status

تصویر-177
در مخزن دیگری با وضعیت تمیز (منبع: مختصر)

یک فایل موجود را بگیرید، مانند این:

تصویر-178
یک فایل نمونه – my_file.py (منبع: مختصر)

و یکی از خطوط آن را تغییر دهید. برای مثال خط دوم را در نظر بگیرید:

تصویر-179
مطالب از my_file.py پس از اصلاح خط دوم (منبع: مختصر)

و اجرا git diff:

تصویر-180
خروجی از git diff برای my_file.py پس از تغییر آن (منبع: مختصر)

خروجی از git diff تفاوت بین را نشان می دهد my_file.pyنسخه در قسمت استیجینگ که در این مورد همان آخرین کامیت است (HEAD) و در دایرکتوری کاری.

من عبارات “دایرکتوری کاری”، “منطقه صحنه سازی” و “تعهد” را در پست قبلی توضیح دادم، بنابراین اگر آن را از دست دادید یا می خواهید حافظه خود را تازه کنید، آن را بررسی کنید.

برای یادآوری، اصطلاحات “منطقه مرحله بندی” و “شاخص” قابل تعویض هستند و هر دو به طور گسترده استفاده می شوند.

تصویر-182
در این حالت، وضعیت تیر کاری با وضعیت شاخص و وضعیت یکسان است HEAD. (منبع: مختصر)

بنابراین برای مشاهده تفاوت بین دایر کاری و ناحیه صحنه سازی، استفاده کنید git diff، بدون هیچ گونه پرچم اضافی.

تصویر-181
بدون سوئیچ، git diff تفاوت بین منطقه صحنه را نشان می دهد (منبع: مختصر)

همانطور که می بینید، git diff هر دو را در اینجا فهرست می کند file A و file B با اشاره به my_file.py. بنابراین file A در اینجا به نسخه اشاره دارد my_file.py در منطقه صحنه سازی، در حالی که file B به نسخه آن در کارگردانی کار اشاره دارد.

توجه داشته باشید که اگر اصلاح کنید my_file.py در یک ویرایشگر متن، و فایل را ذخیره نکنید git diff از تغییراتی که ایجاد کرده‌اید آگاه نخواهند بود، زیرا در راهنمای کار ذخیره نشده‌اند.

چند سوئیچ وجود دارد که می توانیم به آنها ارائه دهیم git diff برای بدست آوردن تفاوت بین کارگردانی کاری و یک commit خاص، یا بین قسمت صحنه و آخرین commit، یا بین دو commit و غیره.

ابتدا یک فایل جدید ایجاد کنید، new_file.txt، و آن را ذخیره کنید. در حال حاضر فایل در dir کار است و در واقع در Git ردیابی نشده است.

تصویر-183
یک فایل جدید ساده به عنوان ذخیره شده است new_file.txt (منبع: مختصر)

حالا این فایل را مرحله و commit کنید:
git add new_file.txt
git commit -m "new file!"

در حال حاضر، وضعیت HEAD مانند وضعیت منطقه استقرار و همچنین درخت کار است:

تصویر-184
وضعیت HEAD همان شاخص و دایر کاری است (منبع: مختصر)

بعد، ویرایش کنید new_file.txt، با اضافه کردن یک خط جدید در ابتدا و یک خط جدید دیگر در پایان:

تصویر-185
در حال اصلاح new_file.txt با افزودن یک خط در ابتدا و دیگری در پایان (منبع: مختصر)

در نتیجه وضعیت به شرح زیر است:

تصویر-186
پس از ذخیره، وضعیت در مسیر کاری با وضعیت شاخص یا متفاوت است HEAD (منبع: مختصر)

یک ترفند خوب استفاده از آن خواهد بود git add -p، که به شما امکان می دهد تغییرات را حتی در یک فایل تقسیم کنید و در نظر بگیرید که کدام یک را می خواهید مرحله بندی کنید.

بنابراین در این مورد، خط اول را به فهرست اضافه کنید، اما خط آخر را نه. برای انجام این کار، می توانید با استفاده از چنگک را تقسیم کنید s، سپس بپذیرید که اولین هانک (با استفاده از y) و نه قسمت دوم (با استفاده از n).

اگر مطمئن نیستید که هر حرف مخفف چیست، همیشه می توانید از a استفاده کنید ? و Git به شما خواهد گفت.

تصویر-187
استفاده کردن git add -p، فقط می توانید اولین تغییر را مرحله بندی کنید (منبع: مختصر)

بنابراین در حال حاضر دولت در HEAD بدون هیچ یک از آن خطوط جدید است. در قسمت صحنه، خط اول را داریم اما خط آخر را نداریم، و در قسمت کاری هر دو خط جدید را داریم.

تصویر-189
وضعیت پس از اجرای تنها خط اول (منبع: مختصر)

اگر استفاده می کنید git diff، چه اتفاقی خواهد افتاد؟

تصویر-188
git diff تفاوت بین شاخص و مسیر کاری را نشان می دهد (منبع: مختصر)

خوب، همانطور که قبلاً گفته شد، شما تفاوت بین ناحیه مرحله بندی و درخت کار را دریافت می کنید.

چه اتفاقی می افتد اگر بخواهید تفاوت بین این دو را بدست آورید HEAD و منطقه صحنه؟ برای آن می توانید استفاده کنید git diff –cached:

تصویر-190
git diff --cached تفاوت بین را نشان می دهد HEAD و شاخص (منبع: مختصر)

و اگر بخواهیم تفاوت بین این دو را داشته باشیم چه می شود HEAD و درخت کار؟ برای آن ما می توانیم اجرا کنیم git diff HEAD:

تصویر-191
git diff HEAD تفاوت بین را نشان می دهد HEAD and the work dir (منبع: مختصر)

برای خلاصه کردن سوئیچ های مختلف برای git diff، این نمودار را ببینید که در صورت نیاز می توانید به عنوان مرجع به آن بازگردید:

تصویر-192
سوئیچ های مختلف برای git diff (منبع: مختصر)

به عنوان یادآوری، در ابتدای این پست استفاده کردید git diff -–no-index. با --no-index سوئیچ می توانید دو فایل را که بخشی از مخزن – یا هر منطقه مرحله بندی نیستند – مقایسه کنید.

اکنون، تغییراتی را که در قسمت مرحله بندی دارید، انجام دهید:

git commit -m "added a first line"

برای مشاهده تفاوت بین این commit و commit والد آن، می توانید دستور زیر را اجرا کنید:

git diff HEAD~1 HEAD

تصویر-194
خروجی از git diff HEAD~1 HEAD (منبع: مختصر)

به هر حال، ما می توانیم حذف کنیم 1 بالا و بنویس HEAD~، و همین نتیجه را بگیرید. استفاده کردن 1 راهی صریح برای بیان اینکه شما به والد اول commit اشاره می کنید است.

پیشنهاد می‌کنیم بخوانید:  یک آرایه در جاوا اسکریپت یک شی استاندارد شده داخلی است که برای ذخیره چندین شی تحت یک نام استفاده می شود. به زبان ساده، می توانید به شی Array به عنوان یک آرایه در هر زبان برنامه نویسی دیگری نگاه کنید. اساساً کلاسی است که یک آرایه (لیست مرتب شده ای از مقادیر) را کپسوله می کند...

توجه داشته باشید که نوشتن commit والد در اینجا، HEAD~1، ابتدا یک تفاوت را نشان می دهد که نشان می دهد چگونه از تعهد والد به تعهد فعلی برسیم. البته، می‌توانم با نوشتن تفاوت معکوس را نیز ایجاد کنم:

git diff HEAD HEAD~1

تصویر-195
خروجی از git diff HEAD HEAD~1 پچ معکوس را تولید می کند (منبع: مختصر)
تصویر-196
سوئیچ های مختلف برای git diff (منبع: مختصر)

یک راه کوتاه برای مشاهده تفاوت بین یک commit و والد آن، استفاده از آن است git show، مثلا:

git show HEAD

این همان نوشتن است:

git diff HEAD~ HEAD

اکنون می توانیم نمودار خود را به روز کنیم:

تصویر-197
مطالب از new_file.txt پس از استفاده git reset --hard HEAD~1 (منبع: مختصر)

به عنوان یادآوری، commit های Git عکس های فوری هستند – از کل فهرست کاری مخزن، در یک نقطه خاص از زمان. با این حال، گاهی اوقات بسیار مفید نیست که یک commit را به عنوان یک عکس فوری در نظر بگیریم، بلکه بیشتر با تغییراتی که این commit خاص معرفی شده است، مفید است. به عبارت دیگر، توسط تفاوت بین یک متعهد والدین به تعهد بعدی.

هنوز مهم است که به یاد داشته باشید که Git کل عکس های فوری را ذخیره می کند و تفاوت به صورت پویا از داده های عکس فوری – با مقایسه درختان ریشه commit و والد آن تولید می شود.

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

نحوه اعمال پچ ها 💪🏻

با استفاده از git diff می توانید پچ را ببینید و سپس می توانید این پچ را با استفاده از آن اعمال کنید git apply.

یادداشت تاریخی 📔

در واقع، به اشتراک گذاری وصله ها، راه اصلی برای اشتراک گذاری کد در روزهای اولیه منبع باز بود. اما اکنون – تقریباً همه پروژه‌ها مستقیماً از طریق درخواست‌های کشش (که در برخی از پلتفرم‌ها «درخواست‌های ادغام» نامیده می‌شوند) به اشتراک‌گذاری تعهدات Git رفته‌اند.

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

از دست دادن تاریخچه تعهد، حل و فصل درگیری ها را دشوار می کند. با غوطه ور شدن عمیق تر در فرآیند آن، بهتر آن را درک خواهید کرد git apply.

یک درخواست ساده

اعمال پچ به چه معناست؟ وقت آن است که آن را امتحان کنید!

خروجی از git diff:

git diff HEAD~1 HEAD

و آن را در یک فایل ذخیره کنید:

git diff HEAD~1 HEAD > my_patch.patch

و reset برای لغو آخرین commit:

git reset –hard HEAD~1

اگر کاملا راحت نیستید git reset، پست قبلی را که به طور عمیق به آن پرداخته است را بررسی کنید. به طور خلاصه، به ما این امکان را می دهد که وضعیت مکان را “بازنشانی” کنیم HEAD اشاره می کند، و همچنین وضعیت شاخص و کارگردانی کار.

در مثال بالا، همه آنها روی حالت تنظیم شده اند HEAD~1، یا Commit 3 در نمودار

پس پس از اجرای دستور reset محتویات فایل به صورت زیر است:

nano new_file.txt

تصویر-198
وصله ای که می خواهید اعمال کنید، همانطور که توسط آن ایجاد شده است git diff (منبع: مختصر)

و ما این پچ را اعمال خواهیم کرد:

nano my_patch.patch

این پچ به git می گوید که خطوط را پیدا کند:

This is a new file
With new content!

این قبلاً خطوط بود 1 و 2، و یک خط اضافه کنید START درست بالای آنها

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

git apply my_patch.patch

و در نتیجه، این نسخه از فایل خود را دریافت می کنید، درست مانند commit که قبلا ایجاد کرده اید:

nano new_file.txt

تصویر-199
مطالب از new_file.txt پس از اعمال پچ (منبع: مختصر)

درک خطوط زمینه 🧑🏻‍🏫

برای درک اهمیت خطوط زمینه، سناریوی پیشرفته تری را در نظر بگیرید. چه اتفاقی می افتد اگر شماره خطوط از زمان ایجاد فایل پچ تغییر کرده باشد؟ 🤔

برای آزمایش، با ایجاد یک فایل دیگر شروع کنید:

تصویر-201
ایجاد یک فایل دیگر – another_file.txt (منبع: مختصر)

این فایل را مرحله بندی و commit کنید:

git add another_file.txt

git commit -m "another file"

حالا این فایل را با افزودن یک خط جدید و همچنین پاک کردن خط قبل از آخرین آن تغییر دهید:

تصویر-202
تغییرات another_file.txt (منبع: مختصر)

تفاوت بین نسخه اصلی فایل و نسخه شامل تغییرات خود را مشاهده کنید:

git diff -- another_file.txt

تصویر-203
خروجی برای git diff -- another_file.txt (منبع: مختصر)

(استفاده کردن -- another_file.txt به Git می گوید که دستور را اجرا کند diff، فقط با در نظر گرفتن another_file.txt، بنابراین تفاوت فایل های دیگر را دریافت نمی کنید.)

این تفاوت را در یک فایل پچ ذخیره کنید:

git diff -- another_file.txt > new_patch.patch

حال، قبل از اعمال تغییرات، حالت خود را به حالت اولیه بازنشانی کنید:
git reset --hard

اگر قرار بود درخواست بدهید new_patch.patch در حال حاضر، آن را به سادگی کار می کند. یک مورد جالب تر را در نظر بگیرید.

تغییر another_file.txt دوباره با اضافه کردن یک خط جدید در ابتدا:

تصویر-209
اضافه کردن یک خط جدید در ابتدای another_file.txt (منبع: مختصر)

در نتیجه، شماره خطوط با نسخه اصلی که در آن پچ ایجاد شده است، متفاوت است. وصله ای که قبلا ایجاد کرده اید را در نظر بگیرید:

تصویر-210
new_patch.patch (منبع: مختصر)

فرض می کند که خط So this is a file اولین خط در است another_file.txt، که دیگر اینطور نیست. بنابراین … خواهد شد git apply کار؟

تصویر-211
git apply پچ را اعمال نمی کند (منبع: مختصر)

خب نه. پچ اعمال نمی شود. اما چرا؟ آیا واقعاً به دلیل تغییر شماره خطوط است؟

برای درک بهتر فرآیندی که Git انجام می دهد، می توانید آن را اضافه کنید --verbose پرچم به git apply، مانند:

git apply --verbose new_patch.patch

تصویر-213
git apply --verbose روندی را که Git برای اعمال پچ طی می کند نشان می دهد (منبع: مختصر)

به نظر می رسد که Git تمام محتویات فایل، به طور خاص، از جمله خط را جستجو کرده است So we are writing an example، که دیگر در فایل وجود ندارد. از آنجایی که Git نمی تواند این خط را پیدا کند، نمی تواند پچ را اعمال کند.

چرا Git کل فایل را جستجو می کند؟ به طور پیش فرض، Git به دنبال آن است 3 خطوط زمینه قبل و بعد از هر تغییر معرفی شده در پچ. اگر سه خط قبل و بعد از خط اضافه شده، و سه خط قبل و بعد از خط حذف شده (در واقع فقط یک خط بعد از آن، زیرا هیچ خط دیگری وجود ندارد) بگیرید – به کل فایل خواهید رسید.

با استفاده از -C بحث و جدل. برای مثال، از Git بخواهید که جستجو کند 1 خط زمینه اطراف، دستور زیر را اجرا کنید:

git apply -C1 new_patch.patch

پچ تمیز اعمال می شود! 🎉

چرا اینطور است؟ دوباره پچ را در نظر بگیرید:

تصویر-210
new_patch.patch (منبع: مختصر)

هنگام اعمال پچ با -C1 گزینه، Git به دنبال خطوط است:

It has some really nice lines
Like this one

به منظور اضافه کردن خط !!!This is the new line I am adding!!! بین این دو خط این خطوط وجود دارند (و مهمتر از همه، آنها یکی پس از دیگری ظاهر می شوند). بنابراین Git می تواند با موفقیت خط بین آنها را اضافه کند، حتی اگر شماره خطوط تغییر کرده باشد.

به طور مشابه، Git به دنبال خطوط خواهد بود:

And we are now learning about Git
So we are writing an example
Git is lovely!

همانطور که Git می تواند این خطوط را پیدا کند، Git می تواند خط وسط را پاک کند.

اگر یکی از این خطوط را تغییر دادیم، مثلاً تغییر کرد And we are now learning about Git به And we are now learning about patches in Git، سپس Git نمی تواند رشته بالا را پیدا کند و بنابراین پچ اعمال نمی شود.

خلاصه

در این پست یاد گرفتید که تفاوت چیست و تفاوت بین diff و patch چیست. شما یاد گرفتید که چگونه وصله های مختلف را با استفاده از سوئیچ های مختلف برای ایجاد کنید git diff.

شما همچنین یاد گرفتید که خروجی چیست git diff به نظر می رسد و چگونه ساخته شده است. در نهایت، نحوه اعمال وصله ها و به طور خاص اهمیت زمینه را یاد گرفتید.

درک تفاوت ها یک نقطه عطف اصلی برای درک بسیاری از فرآیندهای دیگر در Git است – به عنوان مثال، ادغام یا تغییر پایه.

در آموزش های آینده، از دانش خود در این پست برای شیرجه زدن به سایر حوزه های Git استفاده خواهید کرد.

درباره نویسنده

Omer Rosenbaum مدیر ارشد فناوری Swimm است. او نویسنده کانال کوتاه یوتیوب است. او همچنین کارشناس آموزش سایبری و بنیانگذار آکادمی امنیتی چک پوینت است. او نویسنده شبکه های کامپیوتری (به زبان عبری) است. می توانید او را در آن پیدا کنید توییتر.

مراجع اضافی

  • لیست پخش YouTube Git Internals — توسط Brief.
  • پست قبلی Omer در مورد داخلی Git.