سرفصل‌های آموزشی
آموزش کاربردی گیت برای برنامه نویسان
Git Reset چیست؟ بخش اول

Git Reset چیست؟ بخش اول

گیت reset چیست؟

دستور git reset یک ابزار پیچیده و چند منظوره برای لغو تغییرات است. این دستور سه فرم اصلی فراخوانی دارد. این فرم ها به ورودی های خط فرمان --soft, --mixed, --hard مربوط می شوند. سه ورودی که هر کدام مربوط به سه مکانیزم مدیریت داخلی git می شود که عبارت اند از: درخت commit (HEAD)، Staging index و مسیر جاری.

Git reset و سه درخت git

برای درک درست از استفاده ی git reset، ابتدا باید سیستم های مدیریت داخلی git را درک کنیم. گاهی اوقات این مکانیزم ها را "سه درخت git" می نامند. واژه ی درخت ممکن است شما را به اشتباه بیاندازد، همانطور که آنها ساختار داده ای درختی دقیقی نیز ندارند. با این حال git از آنها برای پیگیری و ویرایش یک جدول زمانی از ساختارهای داده ای مبتنی بر گره و اشاره گر استفاده می کند. بهترین راه برای شرح دادن این مکانیسم ها، ایجاد یک تغییر در یک مخزن و دنبال کردن سه درخت آن است. 

برای شروع، ما یک مخزن جدید را با دستور های زیر ایجاد خواهیم کرد:

$ mkdir git_reset_test
$ cd git_reset_test/
$ git init .
Initialized empty Git repository in /git_reset_test/.git/
$ touch reset_lifecycle_file
$ git add reset_lifecycle_file
$ git commit -m"initial commit"
[master (root-commit) d386d86] initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 reset_lifecycle_file

مثال بالا یک مخزن git را بایک فایل خالی جدید به نام reset_lifecycle_file ایجاد می کند. در این مرحله از مثال، مخزن یک commit با شناسه d386d86 دارد که حاوی اضافه شدن فایل reset_lifecycle_file است. 

مسیر جاری (Working Directory)

درخت اولی که در این مثال بررسی خواهیم کرد "مسیر جاری" است. این درخت با فایل سیستم محلی همگام سازی شده است و نماینده ی تغییرات ایجاد شده به محتوا در فایل ها و پوشه ها است.

 $ echo 'hello git reset' > reset_lifecycle_file
 $ git status 
 On branch master 
 Changes not staged for commit: 
 (use "git add ..." to update what will be committed) 
 (use "git checkout -- ..." to discard changes in working directory) 
modified: reset_lifecycle_file

در مخزن نسخه ی نمایشیمان، ما برخی از مطالب را در فایل reset_lifecycle_file تغییر می دهیم. اجرای دستور git status نشان می دهد که git از تغییرات در فایل آگاه است. این تغییرات در حال حاضر بخشی از درخت اول " مسیر جاری " است. از دستور git status می توان برای نشان دادن تغییرات در مسیر جاری استفاده کرد. تغییرات با استفاده از پیشوند modified با رنگ قرمز نمایش داده می شود.

Staging index

بعد از مسیر جاری درخت "Staging index" را بررسی می کنیم. این درخت تغییرات مسیر جاری را دنبال می کند، که با git add برای ذخیره شدن در commit بعدی ارتقا یافته است. این درخت یک مکانیزم ذخیره سازی پیچیده داخلی است. git به طور کلی تلاش می کند تا جزئیات پیاده سازی Staging index را از کاربر پنهان کند.

برای مشاهده ی دقیق وضعیت  Staging index باید از دستور کمتر شناخته شده ی git ls-files استفاده کرد. دستور git ls-files در اساس یک ابزار اشکال زدایی برای بازرسی وضعیت از درخت Staging index است.

git ls-files -s
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   reset_lifecycle_file

در اینجا دستور git ls-files را همراه با گزینه ی -s یا --stage اجرا کرده ایم. بدون گزینه -s خروجی دستور git ls-files به سادگی فهرستی از نام فایل ها و مسیرهایی است که در حال حاضر بخشی از index هستند. گزینه ی -s اطلاعات بیشتری درمورد فایل های موجود در Staging index نمایش می دهد. این اطلاعات حالت bits محتویات stage، نام شیء و شماره ی stage است. در اینجا ما به نام شیء علاقه مند هستیم، که برابر با مقدار دوم (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) است. این یک نمونه ی استاندارد از هش SHA-1 git از محتوای فایل هاست. تاریخچه ی commit، هش SHA خود را برای شناسایی اشاره گرها به منظور استفاده از commit و مرجع آن ذخیره می کند و index Staging دارای هش خودش برای ردیابی نسخه های فایل ها در index است. 

در ادامه، ما فایل reset_lifecycle_file که اصلاح شده بود را به index Staging منقل می کنیم.

$ git add reset_lifecycle_file 
$ git status 
On branch master Changes to be committed: 
(use "git reset HEAD ..." to unstage) 
modified: reset_lifecycle_file

در اینجا با اجرای دستور git add reset_lifecycle_file فایل نام برده شده به index Staging اضافه می شود. اجرای دستور git status در حال حاضر reset_lifecycle_file را به رنگ سبز در زیر "تغییرات commit" نمایش می دهد. مهم است که توجه داشته باشید که دستور git status نماینده ی درست و واقعی index Staging نیست. خروجی دستور git status تغییرات بین تاریخچه ی commit و index Staging را نمایش می دهد. بیایید محتوای index Staging را در این مرحله بررسی کنیم.

$ git ls-files -s 
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

ما  می توانیم ببینیم که هش SHA برای فایل reset_lifecycle_file ازe69de29bb2d1d6434b8b29ae775ad8c2e48c5391  به d7d77c1b04b5edd5acfc85de0b592449e5303770  به روز شده است.

تاریخچه ی commit

درخت آخر تاریخچه ی commit است. دستور git commit تغییرات را به یک عکس فوری دائمی اضافه می کند که در تاریخچه ی commit قرار می گیرد. این عکس فوری همچنین شامل وضعیتی از index Staging در زمان انجام commit است.

$ git commit -am"update content of reset_lifecycle_file"
[master dc67808] update content of reset_lifecycle_file
1 file changed, 1 insertion(+)
$ git status
On branch master
nothing to commit, working tree clean

در اینجا ما یک commit با پیام "update content of resetlifecyclefile" ایجاد کرده ایم. این تغییرات به تاریخچه ی commit اضافه شده است. اجرای دستور git status در این لحظه نشان می دهد که هیچ تغییری در هیچ یک از درختان وجود ندارد. اجرای دستور git log تاریخچه ی commit را نشان می دهد. حالا که ما این تغییرات را از راه سه درخت دنبال کردیم می توانیم شروع به استفاده از git reset کنیم.

Git reset چگونه کار می کند

به طور سطحی، git reset در رفتار مشابه git checkout است. در حالی که git checkout تنها روی اشاره گر HEAD کار می کند، git reset با اشاره گر HEAD و اشاره گر شاخه ی جاری کار می کند. برای نشان دادن بهتر این رفتار، مثال زیر را در نظر بگیرید:

این مثال یک دنباله ای از commit ها را در شاخه ی  master نشان می دهد. اشاره گر HEAD و اشاره گر شاخه ی master در حال حاضر اشاره به commit، d دارند. حالا بیایید دو دستور git checkout b و git reset b را اجرا کنیم و نتیجه را مقایسه کنیم.

git checkout b

با اجرای دستور git checkout اشاره گر شاخه ی master هنوز به d اشاره می کند و اشاره گر HEAD منتقل شده است و در حال حاضر به commit b اشاره می کند. مخزن در حال حاضر در وضعیت "detached HEAD" است.

git reset b

با اجرای دستور git reset هر دو اشاره گر به commit  مشخص، یعنی commit b اشاره می کنند.

افزون بر به روز رسانی اشاره گرها، git reset وضعیت سه درخت را نیز تغییر می دهد. نشانگر مرجع به طور مداوم تغییر می کند و یک به روز رسانی برای درخت سوم یا همان درخت commit است. ورودی های دستور git reset که شامل --soft، --mixed و --hard می شوند شیوه ی تغییر درخت های Staging index و مسیر جاری را مشخص می کنند.

گزینه های اصلی 

دستور git reset به طور پیش فرض دو مقدار --mixed و HEAD را به عنوان ورودی دارد. این به این معنی است که اجرای دستور git reset معادل با اجرای git reset --mixed HEAD است. در این حالت commit ای که HEAD به آن اشاره می کند، مشخص شده است. به جای HEAD می تواند هر هش SHA-1 دیگری که متعلق به یک commit است مورد استفاده قرار بگیرد.

--hard

این گزینه مستقیم و خطرناک است و اغلب مورد استفاده قرار می گیرد. بعد از اجرای دستور با گزینه ی --hard اشاره گر تاریخچه ی commit به commit مشخص شده اشاره می کند. سپس، Staging index و مسیر جاری بازنشانی می شوند تا با  commit مشخص شده مطابقت پیدا کنند. هر گونه تغییرات در حال انتظار موجود در Staging index و مسیر جاری بازنشانی می شوند تا با وضعیت درخت commit مطابقت پیدا کنند. این به این معنی است که هر کار در حال انتظاری که در حلقه ی Staging index و مسیر جاری قرار بگیرد از بین خواهد رفت.

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

$ echo 'new file content' > new_file
$ git add new_file
$ echo 'changed content' >> reset_lifecycle_file

این دستور های یک فایل جدید به نام new_file ایجاد کرده و آن را به مخزن اضافه می کند. افزون بر این، محتوای فایل RESET_LIFECYCLE_FILE را نیز اصلاح می کند. با اعمال این تغییرات بیاید اکنون با اجرای دستور git status وضعیت مخزن را بررسی کنیم.

$ git status
On branch master
Changes to be committed:
   (use "git reset HEAD ..." to unstage)

new file: new_file

Changes not staged for commit:
   (use "git add ..." to update what will be committed)
   (use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file

در حال حاضر می توانیم ببینیم که تغییرات در حال انتظاری در مخزن وجود دارد. درخت Staging index دارای یک تغییر در حال انتظار برای اضافه کردن new_file است و مسیر جاری دارای یک تغییر در حال انتظار برای تغییرات در فایل reset_lifecycle_file است.

قبل از انجام کار بعدی، بیاید وضعیت Staging index را بررسی کنیم:

$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

می توانیم ببینیم که new_file بهindex  اضافه شده است. ما تغییراتی در فایل reset_lifecycle_file ایجاد کرده ایم اما می بینیم که هش SHA آن در Staging index (d7d77c1b04b5edd5acfc85de0b592449e5303770) بدون تغییر باقی مانده است. این رفتار مورد انتظار بود زیرا که از دستور git add برای اضافه کردن تغییرات این فایل به Staging index استفاده نشده بود.

اکنون بیاید دستور git reset --hard را اجرا کنیم.

$ git reset --hard
HEAD is now at dc67808 update content of reset_lifecycle_file

 و سپس وضعیت مخزن را بررسی کنیم.

$ git status
On branch master
nothing to commit, working tree clean
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

در اینجا یک "بازنشانی سخت" را با استفاده از گزینه ی --hard اجرا کردیم. خروجی git نشان می دهد که اشاره گر HEAD به آخرین commit یعنی commit dc67808 اشاره می کند. سپس با استفاده از دستور git status وضعیت مخزن را بررسی می کنیم. git نشان می دهد که تغییرات در حال انتظاری وجود ندارد. همچنین وضعیت Staging index را بررسی می کنیم و می بینیم که قبل از اضافه شدن new_file بازنشانی شده است. تغییرات ما برای reset_lifecycle_file و افزودن new_file از بین رفتند. به این نکته ی مهم توجه کنید که دیگر این داده های از دست رفته را نمی توان باز گرداند.

جمع بندی

در این قسمت با دستور git reset  و سه درخت گیت (مسیر جاری، تاریخچه ی commit و staging Index) آشنا شدیم و فهمیدیم که این دستور چگونه کار می کند همچنین با شیوه ی کار یکی از گزینه های ورودی این دستور نیز آشنا شدیم.

این قسمت ادامه دارد و در بخش بعدی با گزینه های دیگر این دستور آشنا خواهیم شد و مثال هایی نیز خواهیم زد و همچنین تفاوت استفاده ی دستورهای git reset  و git revert را بررسی خواهیم کرد.

online-support-icon