۱۳ سوال اساسی در رابطه با Git که در مصاحبه از شما می‌پرسند.

مقدمه

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

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

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

۱- چگونه یک commit که push و عمومی شده را برگشت دهیم؟

یک یا چند commit می‌توانند توسط دستور git revert بازگشت داده شوند. این دستور، در ماهیت خود یک commit جدید با patch‌هایی می‌سازد که تفییرات معرفی شده در commit بخصوص را کنسل کند. زمانی که commitای که نیاز به بازگشت دارد، منتشر شده باشد و یا امکان تغییر تاریخچه مخزن وجود نداشته باشد، git revert می‌تواند commit را بازگشت دهد. اجرای دستور زیر امکان بازگشت دو commit را به ما می دهد.

git revert HEAD~2..HEAD

روش دیگر این است که شما یک وضعیت مشخص از commitای که در گذشته انجام داده‌اید را checkout کنید و دوباره از نو commit کنید.

۲- چگونه چند commit آخر خود را به یک commit تبدیل کنید؟

تبدیل چندین commit به یک commit تاریخچه را بازنویسی خواهد کرد و باید با احتیاط انجام شود. هرچند این کار زمانی که شما روی branch ویژگی جدید کار می‌کنید بسیار بدرد بخور هست. برای ترکیب چند commit آخر branch فعلی خود، دستور زیر را اجرا نمائید: (N را با تعداد commitای که میخواهید ترکیب شوند عوض کنید)

git rebase -i HEAD~{N}

با اجرای این دستور یک ویرایشگر با لیستی از این N تعداد پیام commit هرکدام در یک خط مجزا باز خواهد شد. هرکدام از این خطوط با واژه pick شروع شده‌اند. با تغییر pick به squash یا s به Git می‌‌گویید تا commit مورد نظر را با commit قبلی ترکیب کند. برای ترکیب تمام N تعداد commit به یکی باید واژه pick تمام خطوط را به squash تغییر دهید به جز خط اول. با خروج از ویرایشگر، اگر هیچ conflictای رخ ندهد، دستور git rebase به شما این امکان را می‌دهد که یک پیام commit جدید برای commitهای ترکیب شده، بسازید.

۳- چگونه لیستی از فایل‌هایی که در یک commit بخصوص تغییرات داشته‌اند را پیدا کنیم؟

git diff-tree -r {hash}

کد هش commit مورد نظر وارد می‌کنیم و با این کار لیستی از فایل‌های اضافه یا تغییر داده شده در آن commit نشان داده خواهد شد. flagای که در این دستور وجود دارد r- هست که موجب می‌شود، فایل‌ها به صورت مستقل نمایش داده شوند و از نمایش فولدر یا root directory که در آن قرار دارد جلوگیری می‌شود.

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

git diff-tree --no-commit-id --name-only -r {hash}

در اینجا no-commit-id هش‌های commit را از خروجی حذف می‌کند و name-only تنها نام فایل‌ها را به جای مسیرشان نمایش می‌دهد.

۴- چگونه اسکریپتی بنویسیم تا هربار که به مخزن commit جدیدی push شد، اجرا شود؟

به منظور نوشتن اسکریپتی که هر بار با push جدید به مخزن اجرا شود، ابتدا نیاز دارید که یک pre-receive یا یک update و یا یک post-receive هوک (hook) با توجه به آنچه که اسکریپت قرار است اجرا کند، داشته باشید.

هوک pre-receive در مخزن مقصد زمانی اجرا می‌شود که commitها به آن push شوند. هر اسکریپتی که به این هوک متصل باشد، قبل از اینکه اتفاقی بیافتد، اجرا می‌شود. این یک هوک مفید برای اجرای اسکریپت‌هایی است که به اجرای مقررات توسعه کمک می‌کند.

هوک update نیز بسیار رفتاری مشابه هوک pre-receive دارد و همواره قبل از اینکه هر بروزرسانی رخ دهد اجرا می‌شود. البته هوک update یک برا برای هر commitای که push شود، اجرا خواهد شد.

درنهایت هوک post-receive که بعد از بروزرسانی مخزن انجام می‌شود. این هوک مناسب‌ترین حالت برای اجرای اسکریپت‌های ساده هستند که برای سیستم‌های یکپارچه مداوم و ارسال ایمیل اطلاع‌رسانی به نگه‌دارندگان مخزن کاربرد دارد.

هوک‌ها برای مخازن Git به صورت محلی هستند و بروز نمی‌شوند. اسکریپت‌ها نیز هم می‌توانند درون دایرکتوری هوک در git. و یا هرجای دیگری ساخته شوند.

۵- دستور git biscet چیست؟ چگونه می‌توانید از آن برای تشخیص یک باگ (regression - بازگشت) استفاده کنید؟

مکانیزمی نسبتاً سریع توسط Git فراهم شده تا بتوان commitها بد و ناموفق را شناسایی کرد. به جای این‌که کاربر تک تک commitها را بررسی کند تا بتواند یکی را که باعث بوجود آمدن مشکلی در کد شده پیدا کند، دستور git biscet به کاربر این امکان را می‌دهد تا بتوانید یک جستجوی باینری روی کل تاریخچه‌ی مخزن انجام دهد.

با استفاده از دستور git biscet start، مخزن به حالت دونیم (biscet) می‌رود. پس از آن، تنها کاری شما باید انجام دهید، مشخص کردن commitهای بد و خوب است.

git bisect bad # marks the current version as bad 
git bisect good {hash or tag} # marks the given hash or tag as good, ideally of some earlier commit

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

git bisect reset

۶- راه‌های مختلف ارجاع دادن به یک commit چیست؟

در Git هر commit یک هش کد منحصر به فرد دارد. این هش‌ها می‌توانند برای مشخص کردن commitهای متناظر در سناریوهای مختلف (مانند زمانی که سعی دارید یک حالت خاص از کد را با استفاده از دستور (git checkout (hash در پروژه checkout کنید.)

علاوه بر این، نام‌های مستعار نیز برای هر commit توسط Git فراهم شده‌اند تا بتوان به آن‌ها ارجاع داد. همچنین هر تگی که شما در مخزن می‌سازید به راحتی می‌توانند به عنوان یک مرجع قرار گیرند (این دقیقاً کاری است که شماه می‌توانید به جای استفاده از هش‌ها در دستورات Git با تگ انجام دهید). همچنین Git نام‌های مستعاری فراهم کرده که بسته به وضعیت مخزن تغییر می‌کند، مانند HEAD، FETCH_HEAD، MERGE_HEAD و غیره.

همچنین Git به commitها این امکان را می‌دهد تا به یکدیگر به عنوان وابسته ارجاع دهند. برای مثال HEAD~1 به یک commit والد HEAD ارجاع می‌دهد و HEAD~2 به پدربزرگ HEAD و به همین منوال ادامه پیدا می‌کند. زمانی که commit ادغام (merge) داشته باشیم که دارای دو والد باشد، ^ می‌تواند برای انتخاب بین دو والد استفاده شود. برای مثال HEAD^2 می‌تواند برای پیروی از دومین والد استفاده شود.

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


۷- دستور git rebase چیست و چگونه می‌توان از آن برای رفع conflictهای در یک branch ویژگی قبل از merge استفاده کرد؟

به عبارت ساده‌تر، دستور git rebase به شما این اجازه را می‌دهد تا اولین commitای که در branch انجام داده‌اید را به یک نقطه شروع جدید ببرید. برای مثال اگر یک branch ویژگی از master درست شده باشد و بعد از آن master چندین commit دریافت کرده باشد، دستور git rebase می‌تواند به منظور انتقال branch ویژگی به نوک شاخه master استفاده شود. این دستور کاملاً کارآمد تغییراتی که در branch ویژگی انجام شده را به نوک شاخه master اضافه می‌کند و امکان برطرف کردن conflictها را در این مسیر به شما خواهد داد. زمانی‌که این کار انجام شود در انتها به راحتی می‌توان branch ویژگی را با master ادغام کرد.


۸- چگونه شما مخزنی می‌سازید که ابزارهای بررسی صحت کد (code sanity checking tools) را درست قبل از انجام commit اجرا کرده و اگر کد ایرادی داشت از آن جلوگیری کند؟

برای انجام این کار به راحتی می‌توان از یک اسکریپت ساده در هوک pre-commit استفاده کرد. این هوک حتی قبل از این‌که شما بخواهید پیامی برای commit خود بنویسید اجرا خواهد شد. در این اسکریپت می‌توان ابزارهای دیگر را اجرا کرد، مانند linterها و صحت کد رای در تغییراتی که قرار است روی مخزن commit شود بررسی کرد. برای مثال به کد زیر نگاه بیاندازید:

#!/bin/sh
 files=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
 if [ -z files ]; then 
    exit 0 
fi unfmtd=$(gofmt -l $files) 
if [ -z unfmtd ]; then
     exit 0 
fi 
echo “Some .go files are not fmt’d” 
exit 1

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


۹- یکی از هم‌تیمی‌های شما به اشتباه یک branch را حذف کرده و تغییراتش را نیز به مخزن اصلی push کرده. هیچ مخزن دیگری نیز وجود ندارد و هیچ‌یک از هم‌تیمی‌های شما کپی محلی از کد ندارد. چگونه شما این branch را بازیابی می‌کنید؟

در reflog آخرین commit به آن branch را checkout کرده و آن را به عنوان یک branch جدید ثبت می‌کنیم.


۱۰- چگونه یک commit انجام شده در branchای را در یک branch دیگر کپی می‌کنید؟ (برای مثال چگونه یک commitای که برای رفع باگ روی branch منتشر شده انجام گشته را روی یک branch درحال توسعه کپی کنیم؟)

برای انجام این کار باید از دستوری cherry-pick استفاده کرد. این دستور این امکان را فراهم می‌کند تا شما یک commit موجود را به موقعیت یا branch فعلی خود اضافه کنید. پس برای این کار نیا به تغییر وضعیت به branch هدف دارید. دستورهای زیر مثالی از این روش را نشان خواهد داد:

git checkout development 
git cherry-pick {hash of that commit}

این‌کار علاوه بر این همان تغییرات را اضافه می‌کند، یک commit جدید با یک هش جدید بوجود می‌آورد چرا که تغییرات برروی یک مقصد متفاوت انجام شده است.


۱۱- چگونه از cherry-pick برای یک commit ادغام استفاده می‌کنید؟

دستور cherry-pick تفاوت بین branchها را پیدا می‌کند. زمانی که یک commit ادغام متعلق به یک branch دیگر باشد، دارای دو والد و دو لیست تغییرات است.

برای مثال اگر شما یک commit ادغام با مرجع 63ad84c داشته باشید، باید به صورت زیر والد مورد نظر خود را انتخاب کنید، که در اینجا والد شماره ۱ مد نظر است.

git checkout release_branch
git cherry-pick -m 1 63ad84c


۱۲- تفاوت بین دو دستور git pull و git fetch چیست؟

دستور git fetch تنها داده‌ی جدید را از مخزن ریموت دانلود می‌کند، اما هیچ یک از داده‌های دانلود شده را با فایل‌هایی که درحال کار روی آن هستید، ادغام نمی‌کند. تنها فایده آن این است که به شما دیدی از این داده‌ها خواهد داد.

دستور git pull علاوه بر دانلود داده از مخزن ریموت آن‌ها را با فایل‌های محلی که در حال کار روی آن هستید merge می‌کند. این عمل حتی ممکن است موجب بوجود آمدن conflict روی فایل‌هایی که هنوز commit نکردید، شود. می‌توانید از دستور git stash برای مخفی کردن تغییرات محلی خود استفاده کنید.


۱۳- مفهوم conflict در git چیست و چگونه برطرف می‌شود؟

یک conflict زمانی رخ می‌دهد که بیش از یک commit در محلی یکسان یا خطی یکسان از کد رخ داده باشد. در این حالت Git قابلیت تشخیص این‌که اولویت با کدام تغییر است را ندارد. به این اتفاق conflict در git می‌گویند.

به منظور حل conflict در git، باید فایل‌هایی که conflict در آن رخ داده را ویرایش کنیم و از دستور git add برای اضافه کردن آن‌ها به commit استفاده کنیم. بعد از این کار برای commit کردن از دستور git commit استفاده می‌کنیم. همواره git به شما یادآوری میکند که شما در وسط عملیات ادغام قرار دارید و همیشه والدین commit را به درستی مشخص می‌کند.


مطلب آخر

این سوالات بیش‌تر به درد مصاحبه شغلی می‌خورند و سوالات خیلی فنی و گمراه‌کننده‌ای نیستندو تنها به منظور راهنمایی شما فراهم شده‌اند. کاندیداهای درجه یک شغلی شاید قادر به پاسخگویی به همه این سوالات نباشند، همچنین اگر کسی همه این سوالات را بداند به این معنا نیست که درجه یک است.


منبع: https://www.toptal.com/git/interview-questions#form