https://winscript.ir
آموزش مبتدی Git: بخش اول
در این مطلب قصد دارم نحوه کار با گیت را به شما توضیح بدهم. پیش نیاز این مبحث مطلب زیر میباشد:
مفاهیم
همانطور که در پست قبلی اشاره کردم:
"گیت یک سیستم کنترل نسخه برای نظارت بر روی تغییرات اعمال شده در طول توسعه نرم افزار میباشد."
ابتدا با مفهوم کنترل نسخه (Version Control) آشنا خواهیم شد. وظیفه کنترل نسخه ثبت تغییرات و ارائه تاریخچه ای از آن ها است. به شرح تصویر برای جلوگیری از چنین کاری:
اما ویژگی بسیار مهم گیت امکان کار گروهی میباشد و میتوانید ببینید چه تغییراتی توسط چه شخصی در چه زمانی صورت گرفته است. احتمالا قبل از گیت برای کار تیمی از طریق فلش یا آپلود و دانلود کردن فایل جا به جا میکردید که شیوه قابل اطمینانی نیست. کنترل های نسخه دو دسته هستند: متمرکز (Centralized) و توزیع (Distributed) شده. در نوع متمرکز فایل های پروژه و تاریخچه آن بر روی سرور مرکزی نگهداری شده و سیستم ها با اتصال به سرور به داده ها دسترسی پیدا میکنند. ایراد اصلی این نوع کنترل نسخه غیر قابل اعتماد بودن آن است زیرا در صورت اختلال در اتصال به سرور یا قطعی سرور مرکزی، تاریخچه و دیگر ویژگی های کنترل نسخه از دسترس خارج خواهد شد. برای حل این مشکل، کنترل نسخه توزیع شده به میان آمد که فایل های پروژه و تاریخچه آن علاوه بر سرور، بر روی کلاینت کاربر هم قابل دسترس است. با این توضیحات، گیت یک سیستم کنترل نسخه توزیع شده است که از ویژگی های آن میتوان به موارد زیر اشاره کرد:
- سریع و کم حجم
- شاخه بندی
- تاریخچه عملیات
- بازگردانی تغییرات
- مقایسه تغییرات
?مباحث این مطلب در محیط خط فرمان Git Bash توضیح داده شدهاند اما در نرم افزار های دیگر هم قابل استفاده هستند.
تنظیمات
سیستم گیت مانند هر نرم افزار دیگری دارای تنظیمات مختص به خود میباشد. تنظیمات آن به سه بخش تقسیم میشود:
- تنظیم مجزا برای هر پروژه (Local)
- تنظیم کلی برای تمام پروژه های کاربر فعلی (Global)
- تنظیم کلی برای تمام پروژه های کاربران سیستم عامل (System)
یک بار تنظیم Global اکثر نیاز های شما را پاسخ میدهد. این تنظیمات در یک فایل کانفیگ با نام .gitconfig در دایرکتوری یوزر شما (C:\Users\<Username>\.gitconfig) ذخیره میشوند. این فایل از ترمینال Git Bash با این دستور قابل دسترس است:
$ git config --global --edit
اگر در صورت نوشتن این دستور با محیط ترمینال وحشتناکی مواجه شدید حتما دارید از Vim استفاده میکنید:
نگران نباشید برای خروج از Vim متن زیر را تایپ و کلید اینتر را بزنید:
:qa!
توصیه میشود در صورت نصب بودن از Visual Studio Code استفاده کنید:
$ code .gitconfig
تغییرات اعمال شده در تاریخچه گیت شامل نام و آدرس ایمیل کاربر هستند. برای تعریف نام:
$ git config --global user.name 'foo bar'
و برای تعریف ایمیل:
$ git config --global user.email 'foo@bar.baz'
حال اگر دوباره به gitconfig نگاه کنید شاهد ذخیره شدن این تغییرات خواهید بود
برای مشاهده تمام تنظیمات اعمال شده:
$ git config --list
تنظیم Local هم شبیه به Global است با این تفاوت که gitconfig فقط config نامیده شده و داخل پوشه مخفی git در هر پروژه قرار دارد. نوشتن دستور git config بدون --global بصورت local اعمال میشود.
ایجاد
میخواهیم با دستورات یونیکس پوشه ای ایجاد کرده و درون آن یک سیستم گیت راه اندازی کنیم. خوب است که هرزگاهی صفحه ترمینال را تمیز کنیم:
$ clear
بصورت پیش فرض Git Bash در دایرکتوری کاربر یعنی C:\Users اجرا میشود که در یونیکس معادل ~ است. برای رفتن به دسکتاپ میتوان از دستور Change Directory استفاده کرد:
$ cd ~/Desktop
میخواهیم با دستور Make Directory پوشه ای بر روی صفحه دسکتاپ ایجاد کرده و به درون آن برویم:
$ mkdir myGit
و برای رفتن به داخل آن:
$ cd myGit
در صورت نیاز برای خارج شدن از یک فولدر کافی است روبروی دستور cd از دو کاراکتر نقطه استفاده کنید. همچنین میتوان از دستور Print Working Directory از دایکرتوری فعلی اطمینان حاصل کرد:
$ pwd
به پروژه هایی که توسط گیت مدیریت میشوند اصطلاحا ریپو (ابتدای کلمه Repository) گفته میشود. برای ایجاد یک repo خالی کافی است از دستور زیر استفاده کنیم:
$ git init
این دستور یک پوشه مخفی git درون دایکرتوری فعلی ایجاد میکند. با دستور List Structure دیده میشود:
$ ls -a
تا اینجای کار یک ریپو ایجاد کردیم. ریپوی گیت برای ارائه کنترل نسخه نیاز به محتوا برای کار کردن دارد پس:
$ touch note.txt
این دستور یک فایل متنی با نام note و پسوند txt در مسیر جاری ایجاد میکند. (برای حذف هم دستور rm). این فایل در حال حاضر خالی است و محتوایی برای نمایش ندارد اما در هر صورت میخواهیم ایجاد شدن آن را در تاریخچه گیت ثبت کنیم. تمام فایل هایی که در ریپوی گیت ایجاد میشوند Untracked هستند به این معنا که گیت هیچ تعاملی با آن ها ندارد. چطور میتوان از وضعیت هر فایل در گیت خبردار شد؟ با دستور:
$ git status
به متن قرمز رنگ دقت کنید، اسامی فایل های Untracked در اینجا نمایان میشوند. قبل از ثبت شدن فایل ها به تاریخچه گیت، فایل ها باید آماده ثبت شوند که به آن Stage کردن میگویند:
$ git add note.txt
وضعیت فایل از Untracked تغییر کرده و به رنگ سبز در آمده است. حال فایل Stage شده آماده ثبت در تاریخچه گیت میباشد که به آن Commit گفته میشود. عمل Commit نیازمند ارائه توضیحاتی در مورد تغییرات رخ داده در ریپو است که بطور متداول ابتدای آن با فعل امری از عمل رخ داده نوشته میشود:
$ git commit -m "Create note.txt"
اولین کامیت به گیت ارسال شده است و در آینده میتوان وضعیت فایل را به وضعیت کامیت های ثبت شده بازگرداند. این نکته را هم به یاد داشته باشید که مرسوم است متن اولین کامیت "Initial commit" ثبت شود. برای مشاهده کامیت های ثبت شده در ریپو از دستور زیر استفاده کنید:
$ git log
دستور log اطلاعاتی همچون مشخصه کامیت به همراه شاخه متعلق به آن، نام و آدرس ایمیل فرد ثبت کننده، تاریخ دقیق و جزئیات کامیت را چاپ میکند. مشخصه های کامیت با الگوریتم رمزنگاری SHA-1 تولید و Hash میشوند. مشاهده log مرتب و خطی نیز بدین صورت است:
$ git log --oneline
تصویر زیر نحوه ثبت شدن فایل در ریپو را شرح میدهد، آن را به خاطر بسپارید:
در ادامه برای مرور میخواهیم چندین فایل جدید به ریپو اضافه کنیم:
$ touch file1 file2 file3
لزوما نیازی نیست فایل ها پسوندی داشته باشند. همچنین لزومی هم ندارد خودمان فایل ها را ایجاد کنیم، میتوانیم فایل های موجود در سیستم را به پوشه ریپو کپی کنیم.
فایل های جدید در حالت Untracked قرار گرفتند. میتوان تمام تغییرات را همزمان به Staging اضافه کرد:
$ git add .
در صورت منصرف شدن از Stage کردن:
$ git restore --staged .
و مانند قبل برای کامیت فایل های Stage شده:
$ git commit -m "Add multiple files"
کامیت ها دنباله ای از لیست های پیوندی هستند که علاوه بر تغییرات فعلی، کامیت های قبل خود را نیز شامل میشوند. کامیت ها فقط میتوانند یک والد (Parent) داشته باشند مگر اینکه از دو شاخه (Branch) ادغام (Merge) شده باشند.
حذف
حذف یک یا چند فایل از ریپو نیز با کامیت یک مجزا امکان پذیر است. در حالاتی که میخواهید تمام فایل ها را با git add به stage برده و آن ها را کامیت کنید، این دستور جایگزین سریعتری است:
$ git commit -am "Remove file"
اما استفاده از این نوع کامیت دقت کار را کاهش داده و از همین رو استفاده از آن پیشنهاد نمیشود.
کنترل نسخه گیت فقط به حذف و اضافه پرونده ها محدود نمیشود بلکه محتویات درون آن ها را هم نظارت میکند که در ادامه خواهیم دید.
تغییر
میخواهیم تغییری در یکی از فایل ها ایجاد کرده و آن را به تاریخچه گیت اضافه کنیم. یکی از خصوصیات برنامه Visual Studio Code تشخیص خودکار ریپو و باز کردن آن بصورت Workspace میباشد. کافی است داخل دایرکتوری ریپو دستور زیر را بنویسیم:
$ code .
یکی از فایل های Workspace باز شده را از پنل Explorer سمت چپ باز کرده و متنی داخل آن مینویسیم. فایل را با کلید میانبر Ctrl + S ذخیره میکنیم.
سپس دوباره به ترمینال Git Bash رفته و status میگیریم:
همانطور که مشاهده میکنید دیگر متن Untracked نمایان نمیشود زیرا گیت در حال Track کردن تغییرات آن است و متن not staged در اینجا دیده میشود. این بدان معناست که گیت بر روی این فایل نظارت دارد و تغییرات آن را با متن قرمز رنگ modified اعلام میکند. مانند قبل برای ثبت تغییرات:
$ git add note.txt
سعی کنید برای کامیت پیامی بنویسید که حداقل برای خودتان و در پروژه های تیمی برای همه واضح باشد:
$ git commit -m "Update note.txt with greetings"
?دستورات گیت در ترمینال Microsoft Visual Studio Code نیز قابل اجرا هستند:
میخواهیم با یکی دیگر از دستورات مفید گیت آشنا شویم. تغییرات بیشتری داخل note.txt ایجاد میکنیم:
برای اینکه از جرئیات تغییرات اعمال شده مطلع شویم کافی است دستور زیر را بنویسیم:
$ git diff
تغییرات قبل و بعد نمایش داده میشود. دقت کنید تغییرات note.txt در Stage قرار نگرفتهاند. پس از اضافه کردن فایل به Stage اگر از دستور git diff استفاده کنیم با همچین رخدادی روبرو خواهیم شد:
باید برای مشاهده جزئیات تغییرات فایل های Stage شده از دستور زیر استفاده کنیم:
$ git diff --staged
پس از مطمئن شدن از تغییرات آن ها را کامیت میکنیم:
$ git commit -m "Add new lines to note.txt"
از دیگر مشکلات این چنین که ممکن است با آن روبرو شویم هنگام تغییر نام فایل است. از دستور یونیکس Move برای انتقال فایل به فایل دیگری و از این رو تغییر نام آن استفاده میکنیم:
(چند بار جمله قبل رو توی ذهنتون تکرار کنید ? )
$ mv file2 file_new
پارامتر اول نام فایل قدیمی و پارامتر دوم نام فایل جدید است یعنی file2 به file_new تغییر نام داده است:
اما از دید گیت اتفاق دیگری رخ داده به این صورت که file2 حذف و file_new ایجاد شده است.
شیوه صحیح تعریف کردن این عمل تغییر نام به گیت بدین صورت است که ابتدا فایل جدید را به Staging میبریم:
$ git add file_new
و سپس فایل قبلی را پاک میکنیم:
$ git rm file2
توانستیم با موفقیت گیت را از این تغییر با خبر کنیم و حالا نوبت ثبت این تغییر است:
$ git commit -m "Rename file2 to file_new"
به یاد داشته باشید میتوانید تعداد نتایج git log خود را با نوشتن یک عدد مقابل آن محدود کنید:
$ git log -3 --oneline
بازگردانی
همانطور که در ابتدای مطلب اشاره شد، گیت یک سیستم کنترل نسخه است به این معنی که تاریخچه ای از تمام تغییرات نگهداری میکند. این تغییرات قابل بازیابی هستند و گیت میتواند حالت فعلی ریپو را به یک نسخهی قدیمیتر بازگرداند. برای این کار باید از دستور checkout به همراه کل یا بخشی از مشخصه SHA-1 کامیت (مانند مشخصه oneline) استفاده کنیم.
$ git checkout <SHA>
میخواهیم به قبل اضافه شدن خطوط جدید به فایل note.txt بازگردیم. مشخصه آن برای شما متفاوت است.
تا به الان با کلمه HEAD زیاد مواجه شدهایم. در واقع HEAD یک اشاره گر (pointer) به شاخه فعلی است. متن آبی رنگ در تصویر را با دقت نگاه کنید، این اشاره گر با دستور Concatenate هم قابل فراخوانی است:
$ cat .git/HEAD
در حالت عادی HEAD به refs/heads/master
اشاره میکند. فایل note.txt را باز میکنیم.
تمام تغییرات بعد از کامیت مشخصی که به آن checkout کردهایم از بین رفتهاند. تغییرات جدیدی ایجاد و آن را کامیت میکنیم:
پس از تغییر در زمان گذشته میخواهیم به زمان حال برگردیم:
$ git checkout master
اما اتفاقی ناخوشایند در انتظار ما است!
تغییراتی که در گذشته اعمال کردیم به زمان حال انتقال پیدا نکردهاند. فایل note.txt را باز کنید و ببینید.
$ cat note.txt
چه اتفاقی رخ داده است؟
تغییرات detached به شاخه master تعلق ندارند و کامیت 1f0517d به هیچ شاخه ای متصل نیست. میخواهیم تغییراتی که در گذشته اعمال کردیم جایگزین محتوای فعلی note.txt شوند اما checkout کردن کامیت از طریق SHA غیرمتصل ممکن نیست. از تاریخچه HEAD با دستور reflog میتوان آن را پیدا کرد:
$ git reflog
کافی است مشخصه اشاره گر را checkout کنیم:
$ git checkout HEAD@{1}
محتوای فایل را در صورت نیاز تغییر داده و پس از ذخیره آن را کامیت میکنیم:
برای اطمینان log را بررسی میکنیم:
نوبت ایجاد یک شاخه جدید است. برای این کار از سویچ b مقابل دستور checkout استفاده میکنیم:
$ git checkout -b <NAME>
شاخه جدید و مجزا از master ایجاد کردیم. برای دیدن لیست شاخه ها از دستور branch استفاده میکنیم:
$ git branch
متن سبز رنگ و کاراکتر asterisk کنار آن نشانگر شاخه فعلی است. شاخه مانند دو جهان موازی در کنار هم وجود دارند اما سرانجام باید به شاخه اصلی یعنی master ختم شوند. برای اعمال تغییرات شاخه جدید در master باید عمل ادغام (merge) صورت گیرد. برای ادغام به شاخه ای که قرار است عمل merge بر روی آن انجام شود checkout میکنیم. این بار تغییرات اعمال شده بر روی branch قرار دارند و مستقیما قابل دستیابی هستند.
میتوانید برای متوجه شدن این عمل جفت فایل note.txt در master و شاخه جدید را cat کنید:
$ cat note.txt
تغییر شاخه
$ git checkout master
بررسی مجدد
$ cat note.txt
تغییرات temp-fix-note را در master ادغام میکنیم:
$ git merge <temp-fix-note>
اما با مشکل رایجی تحت نام Conflict برخورد خواهیم کرد.
همانطور که مشاهده میکنید گیت پیغام وجود conflict در فایل note.txt را گزارش میدهد. این اتفاق زمانی رخ میدهد که محتوای فایل در شاخه های مختلف احتمال جایگزینی و از بین رفتن محتویات فعلی شوند. اگر از دستور cat دوباره استفاده کنید متوجه خواهید شد که گیت تفاوت این دو فایل را نمایش میدهد:
متن Hello, World در هر دو یکسان است اما از جایی که مشخص گردیده تفاوت دو شاخه دیده میشود. برنامه VS Code این تفاوت را بهتر نمایش میدهد:
$ code note.txt
رفع conflict به عهده خودتان هست. باید یکی از محتویات یا هر دو را پاک کنید. برای سادگی کار تمام متن بعد از Hello, World را پاک و آن را کامیت میکنیم:
$ git commit -a -m "Resolve merge conflict by deleting both"
و در نهایت برای اطمینان عمل ادغام را تکرار میکنیم:
$ git merge temp-fix-note
در بخش بعدی نگاه عمیق تری به شاخه ها و دستوراتی همچون reset و revert خواهیم داشت:
موفق باشید ?
مطلبی دیگر از این انتشارات
آموزش حذف کامل افزونه وردپرس و اطلاعات آن از دیتابیس
مطلبی دیگر از این انتشارات
راهنمای طراحی پلاگین وردپرس
مطلبی دیگر از این انتشارات
رفع مشکل تاریخ فارسی Parsidate در نسخه 5.3 وردپرس