آشنایی با Git: مباحث پیشرفته‌تر (ضمیمه 2)

(Introduction to Git: More Advanced Topics (Appendix 2

25 بهمن 1399
درسنامه درس 6 از سری آشنایی با Git
آشنایی با Git: مباحث پیشرفته تر

در قسمت قبلی از دوره آموزش git برای تصحیح commit ها توضیح دادیم که:

لطفا به یاد داشته باشید که دستور amend-- فقط برای تصحیح آخرین commit استفاده می شود. اگر می خواهید commit های قبل از آن را ویرایش کنید باید از دستور rebase و ساختار خاص آن استفاده کنید.

برای موارد پیچیده تر که می خواهید تمام پروژه به قبل از commit خاصی برگردد که آخرین commit نیست می توانید از git revert نیز استفاده کنید. این دستور تمام تغییرات یک commit را برگردانده و یک commit جدید می سازد. می توانید با کلیدواژه HEAD به آخرین commit  دسترسی داشته باشید:

$ git revert HEAD

برای دیگر commit ها (که آخرین commit نیستند) بهتر است از id خاص خودشان استفاده کنیم:

$ git revert b10cc123

البته استفاده از دستور revert برای این فایل ها ممکن است باعث بروز مشکلاتی هم شود؛ به طور مثال شما فایلی را در یک commit قدیمی تغییر داده اید اما آن فایل در یک commit جدید تر نیز تغییر کرده است! در این صورت git نمی تواند تشخیص دهد که کدام یک را باید تغییر دهد و کدام خطوط، خطوط درست هستند.

حالا باید راه حل این اختلاف را بررسی کنیم...

4. حل اختلاف و ناهمخوانی در Merge

ابتدا باید توجه داشته باشید که این نوع از اختلافات تنها هنگام تصحیح commit ها رخ نمی دهد بلکه هنگامی که قصد دارید دو شاخه را در هم ادغام کنید و یا اینکه پروژه دیگری را pull کنید احتمالا با خطای تداخل روبرو خواهید شد. برخی از این اختلافات به صورت خودکار توسط git حل می شوند اما در بقیه موارد فرد توسعه دهنده باید انتخاب کند که کدام کدها حذف شده و کدام کد ها باقی خواهند ماند. متاسفانه در چنین شرایطی راه خودکاری برای حل مشکل وجود ندارد و باید به صورت دستی آن را حل کنید.

فرض کنید دو توسعه دهنده به نام Tim و John داشته باشیم. هر دوی این توسعه دهندگان در حال نوشتن تابعی هستند که تمام اعضای یک آرایه را نمایش دهد، اما هر کدام در شاخه مربوط به خود است. حالا پروژه جلو رفته است و ما می خواهیم شاخه هایjohn_branch  و tim_branch را در هم ادغام کنیم.

تابع John بدین شکل نوشته شده است:

// Use a for loop to console.log contents.
for(var i=0; i<arr.length; i++) {
    console.log(arr[i]);
}

اما Tim ترجیح داده است که از forEach استفاده کند:

// Use forEach to console.log contents.
arr.forEach(function(item) {
    console.log(item);
});

هر دوی آن ها تغییرات خود را commit کرده اند و اگر بخواهیم دو شاخه را در هم ادغام کنیم با خطای زیر روبرو می شویم:

$ git merge tim_branch 

Auto-merging print_array.js
CONFLICT (content): Merge conflict in print_array.js
Automatic merge failed; fix conflicts and then commit the result.

این پیام به ما می گوید که کدها حاوی اختلاف یا تضاد هستند و به شکل فعلی شان نمی توانند در هم ادغام شوند. به زبان ساده تر git نتوانسته اختلاف بین دو شاخه را به صورت خودکار حل کند بنابراین شما و توسعه دهندگان تیم خود باید این مشکل را به صورت دستی حل کنید.

پیام خطایی که به ما داده شده است به فایل print_array.js اشاره می کند و می گوید که اختلاف در این فایل است. اگر این فایل را باز کنید متوجه می شوید که git قسمت هایی که با هم در تضاد هستند را علامت گذاری کرده است:

<<<<<<< HEAD
// Use a for loop to console.log contents.
for(var i=0; i<arr.length; i++) {
    console.log(arr[i]);
}
=======
// Use forEach to console.log contents.
arr.forEach(function(item) {
    console.log(item);
});
>>>>>>> Tim's commit.

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

از آنجایی که این یک پروژه واقعی نیست برای ساده تر شدن آموزش راه حل دوم (یعنی حذف هر دو و نوشتن یک کد جدید) را انتخاب میکنیم:

// Not using for loop or forEach.
// Use Array.toString() to console.log contents.
console.log(arr.toString());

دقت داشته باشید که علامت های اضافه شده توسط git (علامت ===== و >>>>>>> و <<<<<<<) را حذف کرده ایم تا git بفهمد که مشکل را حل کرده ایم. زمانی که همه چیز را نوشتیم باید یک commit دیگر ایجاد کنیم:

$ git add -A
$ git commit -m "Array printing conflict resolved."

همانطور که می شود تصور کرد انجام این پروسه در پروژه های بزرگ بسیار طاقت فرسا خواهد بود بنابراین اکثر توسعه دهندگان ترجیح می دهند از GUI Client (کلاینت گرافیکی Git) استفاده کنند که کار را بسیار آسان تر می کند. برای اجرای GUI از می‌توانید دستور git mergetool استفاده کنید.

5. ایجاد gitignore.

در هر پروژه ای فایل ها یا پوشه هایی وجود دارند که هیچ وقت commit نخواهند شد و معمولا به خود توسعه دهنده و کار های او مربوطند. برای اینکه مطمئن شویم به طور تصادفی یا از روی اشتباه آن ها را در پروژه خود قرار نداده و Commit نمیکنیم (مثلا اشتباها از دستور git add -A تمام فایل ها و پوشه های یک دیرکتوری را اضافه خواهد کرد) یک فایل به نام gitignore. می سازیم:

  1. به طور دستی یک فایل به نام gitignore. ایجاد کرده و آن را درون دیرکتوری پروژه خود قرار دهید.
  2. در این فایل نام پوشه ها و فایل هایی را وارد کنید که می خواهید add نشوند (نام هر فایل یا پوشه باید در یک خط جدید باشد، بنابراین در یک خط و پشت سر هم ننویسید).
  3. خود فایل gitignore. را add و سپس commit و push کنید.

برای نمونه چند مثال از فایل هایی که بهتر است نادیده گرفته شوند را ذکر می کنم:

  • فایل های log
  • بیلدهای task runner
  • پوشه node_modules در پروژه های js
  • پوشه هایی که توسط IDE ها (مثل Netbeans و IntelliJ) ساخته می شوند
  • یادداشت های شخصی توسعه دهنده
  • و ...

فایل نمونه gitignore. که تمام موارد بالا را دارد بدین شکل است:

*.log
build/
node_modules/
.idea/
my_notes.txt

علامت ستاره (*) یعنی هر چیزی به جای آن باشد مهم نیست. مثلا *.log یعنی هر فایلی که آخر نامش log. داشته باشد. وجود علامت اسلش (/) در انتهای برخی از موارد بالا هم به معنی پوشه بودن آن هاست تا تمام فایل های درونشان نیز نادیده گرفته شوند.

این مقاله آخرین مقاله از سری مقالات دوره آموزش git بود. برخی از منابع کاربردی git برای یادگیری بیشتر:

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آشنایی با Git توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.