نگهداری تغییرات فایل در گیت - تغییرات اتمیک!

امروز میخوام براتون از تجربه ۱ سال و خورده ای که توی یک شرکت استارت‌آپ داشتم بگم. خیلی نمیخوام در مورد همه جوانب صحبت کنم بلکه میخوام مستقیم برم سر اصل مطلب و اونم ورژن کنترلی به اسم گیت (GIT). اگر در مورد ورژن گیت اطلاعاتی ندارین اینجا میتونین توضیحات خیلی خوبی در موردش بخونین.بزارین یکم مقدمه چینی کنم. خب من به عنوان اولین برنامه نویس این پروژه استخدام شدم و از اونجایی که تا مدتی داشتم تنهایی روی این پروژه کار می‌کردم و کسی هم نبود روی کارم مدیریت کنه منم هرجوری دلم میخواست داشتم توی پروژه مانور میدادم و از هر ابزاری دوست داشتم استفاده کردم که بعضی جاها به گریه کردن افتادم به خاطر مانورام ولی با یک هفته مرتب سازی فایل های پروژه بخیر گذشت.بعد از مرتب سازی پروژه به خاطر این که خیالم جمع بشه که گندی بالا نیومده و فایل ها و بخش های پیاده سازی شده مثل قبل کار میکنن یک پروژه جدید روی گیت‌هاب ساختم و پروژه جدیده رو کنار پروژه قبلیه اونجا آپلود کردم.(یادش بخیر ما هم میتونستیم پروژه خصوصی روی گیت‌هاب داشته باشیم)یک روز دیدم خیلی تاریخچه گیت بهم ریخته داره میشه و همه کامیت ها و ادغام ها دارن قاطی میشن و برگردودن یک سری از چیزا دیگه تقریبا غیر ممکنه. از طرفی هم همش میدیدم که پروژه های اوپن سورس خفنی که روی گیت هابه و چند هزار نفر دارن روش کار میکنن همیشه تاریخچه گیت تمیزی داره و این اصلا برام قابل درک نبود. مطمعن بودم یک جای کارم داره میلنگه (که همینطور هم بود).اینجوری که پیش میرفتم همیشه به یک چیزی مثل شکل زیر میرسیدم آخرش!تاریخچه شلوغ گیتمطمعنن شما هم مثل من چند باری با یک چیزی مثل شکل بالا مواجه شدین و چند بار سعی کردین متوجه بشین چی شده! واسه همین شروع کردم گوگل کردن تا با واژه کامیت اتمیک (Atomic Commit) آشنا شدم و رفتم دنبالش . دقیقا همون چیزی بود که دنبالش بودم. بهتره شروع کنیم.معنی اتمیک (Atomic): Atomic: Forming a single irreducible unit or component in a larger systemبه زبون خودمونی میگه یک قسمت از یک سیستم رو اینقدر کوچیک کنیم که از اون کوچکتر نشه!خب اولش منطقی بود و توی گیت هم همین کارو میکنیم و اینقدر کامیت رو کوچیک میکنم تا به کوچک ترین کامیت معنی دار ممکنه برسه (ممکنه این کامیت کوچک شامل چند تا فایل مرتبط به هم نیز بشه).شاید یک سوال پیش بیاد که خب این حجم از کامیت بازم اعصاب خورد کنه. به اونم میرسیم!خوب داشتم توی این سایت چرخی میزدم. مثال خیلی قشنگی زده بود و منم بهتر از این بلد نبودم مثال بزنم.بیاین فکر کنیم یکی داره رو پروژه کار میکنه و بخش ظاهری سایت دستشه و یک کامیت میزنه مثل شکل زیرAdd FrontEnd راستش این خیلی وحشتناکه مخصوصا برای کسی که میخوای کد رو بازبینی کنه. حالا برای این که مشکل حل بشه از دو تا چیز کمک میگیریم: شاخه (branch) و کامیت اتمیکحالا بیایم یک شاخه (Branch) جدید به اسم frontEnd بسازیم و تغییرات رو اونجا اضافه کنیم. مثل زیر:Create HTML for formStyle formAdd HTML5 validationFix unrelated JS bugAdd ajax submit to form with mock server results from PHPAdd JS validation to formSend form results via emailLog form results to databaseStyle form validation and success/error messagesخب الان دیگه خیلی راحت تره برای من اگر جایی موقع اضافه کردن یک ویژگی جدید به سیستم، خراب کاری کردم پیداش کنم و به راحتی میتونم اون کامیت رو برگردونم (revert). مورد بعدی اینه که برای هر کامیت شما فضای مخصوص به خودش رو دارین تا به غیر از متنی که برای کامیت انتخاب میکنین یک سری توضیحات در مورد کارهای انجام شده توی اون کامیت هم بنویسین. خوب این به شما این قدر رو میده که در مورد هر کامیت اتمی که دارین یک فضای مخصوص داشته باشین که در مورد اون کار هایی که توی اون کامیت انجام دادین یک دل سیر توضیح مفید بدین بدون این که نگران باشین با جای دیگه اشتباه گرفته بشه.بعد از اینکه این بخش ها گذشت اگر شما زمانی در آینده نه چندان دور خواستین ببینین که برای چی اون کد رو اونجا نوشتین و چه استفاده ای داشته خیلی راحت میتونید تاریخچه اون فایل توی گیت رو جست و جو کنید و شجره نامشو در بیارین. البته این بیشتر به درد مدیر پروژه و بالا دستیا میخوره که میخوان کد شمار رو برسی کنن چون هر برنامه نویسی میدونه برای چی کدی رو نوشته (معمولا).خب تا اینجا که همه چی مثل قبله فقط با این تفاوت که نه تنها تاریخچمون بهتر نشد بلکه داره شلوغ تر هم میشه. من هم اول همین حس رو داشتم و اصلا درک نمیکردم تا این که یک مفهوم دیگه ای آشنا شدم به اسم تاریخچه خطی.این تاریخچه اصل و اصولش بر این مبناست که هر شاخه باید بعد از ادغام شاخه قبل ایجاد شده باشه و توی گیت ما نباید تاریخچه ای داشته باشیم که از یک کامیت چند تا شاخه خارج شده باشه و بعد جلو تر به شاخه ادغام شده باشند. همینطور که توی عکس بالا مشاهده میکنین، گیت سمت چپ میشه تاریخچه غیر خطی و سمت راست میشه خطی. خب تا اینجا اوکیه ولی اولش ذهن آدم اینجوری درگیر میشه که من چطور میتونم تاریخچه خطی داشته باشم وقتی چند نفر همزمان دارن روی چند تا ویژگی به صورت جدا کار میکنن. برای منم اولش نمیشد تا اینکه با rebase آشنا شدم. من بهش میگم معجزه تمیز کننده. Rebaseخوب از اونجایی که گیت به ما یک ابزاری میده که پروژه و تغییرات رو به صورت یک گراف مدیریت کنیم. و زمانی که ما یک شاخه جدید ایجاد میکنیم. پدر اولین کامیت ما توی شاخه یکی از کامیت های شاخه پدره. rebase اومده که به ما این لطف رو بکنه و برای ما جای پدر شاخمون رو عوض کنه.خب همونجور که توی عکس مشخصه، ابزار rebase میاد و برای ما این کار رو انجام میده که پدر شاخمون رو اخرین کامین شاخه پدر قرار میده و این به ما این قدرت رو میده که بتونیم تاریخچه ای خطی داشته باشیم. از طرفی هم میتونیم آخرین تغییراتی که روی کد اومده رو هم داشته باشیم و با کدی که نوشتیم تست کنیم و ببینیم آیا مشکلی وجود داره یا نه. فقط یادتون باشه اگر شاختون رو از قبل روی منبع فرستادین الان باید force push کنید به دلیل این که اولین کامیت شاختون تغییرات داشته و با منبع یکی نیست.خب تا اینجا همه چی عالی شد ولی هنوز یک چیز ممکنه مشکل داشته باشه و اونم طولانی بودن لیست کامیت هایی که توی یک شاخه انجام شده. در اینجا هم ابزاری به من کمک کرد به اسم squash.Squashاین ابزار میاد برای ما این ویژگی رو فراهم میکنه که کامیت هایی که ما مشخص کردیم رو برامون یکی میکنه. دیگه چی از این بهتر، هم خطی هم تک کامیت. اینجوری هم تاریخچه خطیه و هم کوچیک و خلاصه. حالا سوالی که پیش میاد اینه که اون همه توضیحی که واسه کامیت ها نوشتیم پس چی؟ خب همشون پاک میشن و اشکالی هم نداره به دلیل این که کد شما داره وارد منبع میشه و معمولا توضیحات داخل هر کامیت تا زمانی مورد استفاده قرار میگیرن که هنوز کد وارد منبع نشده (این مساله ممکنه توی جاهای مختلف فرق کنه و بعضی جاها از توضیحات به عنوان مستند استفاده کنند.). بعد از این که کد وارد منبع شد یعنی هیچ مشکلی نداشته و همه تست ها انجام شده و تمام.حالا شما هم میتونید مثل ما با یک ترفند کوچیک ابتدا تمام کامیت های شاختون رو squash کنین و بعد از اون شاخه پدر رو هم روی این شاخه rebase کنین. اینطوری تغییراتتون به آخر شاخه master اضافه میشه و هر دو شاختون به یک وضعیت یکسان میرسن و به راحتی میتونین شاخه ای داشتین روش کار میکردین رو پاک کنین چون همه تغییرات این شاخه رو الان شاخه پدر هم داره و تغییرات این شاخه به اخر شاخه پدر اضافه شده. تاریخچه خطیاینجوری شد که ما تونستیم یک تاریخچه مرتب داشته باشیم.مرورکلیکامیت اتمیک رو برسی کردیم و گفتم به تغییرات کد خوانایی میده و راحت تر قابل دنبال کردنه.دلیل استفاده از کامیت اتمیک رو برسی کردم ودیدیم چقدر برای اونایی که کد دیگران رو برسی میکنند میتونه مفید باشه.از rebase جهت تغییر پدر شاخه ای که داریم روش کار میکنیم استفاده کردیم.از sqash جهت یکی کردن کامیت ها بعد از اتمام کار استفاده کردیم که تاریخچه خلاصه تری داشته باشیم.دوباره از rebase استفاده کردیم که تغییراتمون رو بفرستیم روی شاخه پدر.