Chapters ▾ 2nd Edition

10.3 (Git Internals) - مراجع گیت (Git References)

مراجع گیت (Git References)

اگر می‌خواستید تاریخچه‌ی مخزن خود را از یک کمیت مشخص، مثلاً 1a410e، ببینید، می‌توانستید چیزی مثل git log 1a410e را اجرا کنید تا آن تاریخچه را نمایش دهد، اما در این حالت باید به خاطر می‌سپردید که 1a410e همان کمیتی است که می‌خواهید به‌عنوان نقطه‌ی شروع آن تاریخچه استفاده کنید. به‌جای آن، راحت‌تر است اگر فایلی داشته باشید که بتوانید آن مقدار SHA-1 را تحت یک نام ساده ذخیره کنید تا بتوانید از آن نام ساده به‌جای مقدار خام SHA-1 استفاده کنید.

در گیت، این نام‌های ساده «مراجع» یا “refs” نامیده می‌شوند؛ فایل‌هایی که آن مقادیر SHA-1 را نگه می‌دارند را می‌توانید در دایرکتوری .git/refs پیدا کنید. در پروژه‌ی فعلی، این دایرکتوری فاقد فایل است، اما یک ساختار ساده دارد:

$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f

برای ساختن یک مرجع جدید که به شما کمک کند محل آخرین کمییتان را به‌خاطر بسپارید، فنی‌ترین کارِ ممکن می‌تواند چیزی به این سادگی باشد:

$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master

حال می‌توانید به‌جای مقدار SHA-1 در دستورات گیت از مرجع head که همین الآن ساخته‌اید استفاده کنید:

$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 Third commit
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit

تشویق نمی‌شوید که فایل‌های مرجع را مستقیماً ویرایش کنید؛ به‌جای آن، گیت دستور امن‌تری به‌نام git update-ref را فراهم می‌کند تا اگر خواستید یک مرجع را به‌روزرسانی کنید از آن استفاده کنید:

$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

در واقع، این همان چیزی است که شاخه (branch) در گیت هست: یک اشاره‌گر یا مرجع ساده به سرِ یک خط کاری. برای ایجاد شاخه‌ای بر روی کمییت دوم، می‌توانید این کار را انجام دهید:

$ git update-ref refs/heads/test cac0ca

شاخه‌ی شما فقط شامل کار از آن کمییت به بعد خواهد بود:

$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit

حالا، پایگاه‌داده‌ی گیت شما از نظر مفهومی چیزی شبیه به این به‌نظر می‌رسد:

Git directory objects with branch head references included
نمودار 176. Git directory objects with branch head references included

وقتی دستوراتی مثل git branch <branch> را اجرا می‌کنید، گیت اساساً همان دستور update-ref را اجرا می‌کند تا SHA-1 آخرین کمییت شاخه‌ای که روی آن هستید را در هر مرجع جدیدی که می‌خواهید ایجاد کنید قرار دهد.

نشانگر HEAD (The HEAD)

سؤال این است که وقتی git branch <branch> را اجرا می‌کنید، گیت چگونه SHA-1 آخرین کمییت را می‌داند؟ پاسخ فایل HEAD است.

معمولاً فایل HEAD یک مرجع نمادین (symbolic reference) به شاخه‌ای است که در حال حاضر روی آن قرار دارید. منظور از مرجع نمادین این است که برخلاف یک مرجع معمولی، این فایل حاوی اشاره‌ای به یک مرجع دیگر است.

 با این حال در برخی موارد نادر، فایل HEAD ممکن است حاوی مقدار SHA-1 یک شیء Git باشد.
این زمانی اتفاق می‌افتد که شما یک تگ، کامیت یا شاخهٔ ریموت را checkout می‌کنید، که مخزن شما را در وضعیت "detached HEAD" قرار می‌دهد.

اگر به محتویات این فایل نگاه کنید، معمولاً چیزی شبیهِ این خواهید دید:

$ cat .git/HEAD
ref: refs/heads/master

اگر دستور git checkout test را اجرا کنید، Git این فایل را به این شکل به‌روزرسانی می‌کند:

$ cat .git/HEAD
ref: refs/heads/test

وقتی که git commit را اجرا می‌کنید، یک شیء کامیت ساخته می‌شود و والد آن شیء کامیت، مقداری SHA-1 خواهد بود که مرجع در HEAD به آن اشاره می‌کند.

شما همچنین می‌توانید این فایل را دستی ویرایش کنید، اما باز هم یک فرمان ایمن‌تر برای این کار وجود دارد: git symbolic-ref. می‌توانید مقدار HEAD را با استفاده از این فرمان بخوانید:

$ git symbolic-ref HEAD
refs/heads/master

همچنین می‌توانید مقدار HEAD را با همان فرمان تنظیم کنید:

$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test

نمی‌توانید یک ارجاع نمادین را خارج از سبک refs قرار دهید:

$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/

تگ‌ها (Tags)

همین‌الان دربارهٔ سه نوع اصلی اشیاء Git (_blob_ها، _tree_ها و _commit_ها) صحبت کردیم، اما یک نوع چهارم هم وجود دارد. شیء tag بسیار شبیه به شیء commit است — شامل اطلاعات تگ‌زننده (tagger)، تاریخ، پیغام و یک نشانگر است. تفاوت اصلی این است که یک شیء تگ معمولاً به یک commit اشاره می‌کند تا به یک tree. این مانند یک ارجاع شاخه است، اما هرگز حرکت نمی‌کند — همیشه به همان کامیت اشاره می‌کند و برای آن نامی دوستانه‌تر فراهم می‌آورد.

همان‌طور که در مقدمات گیت (git basics chapter) توضیح داده شد، دو نوع tag وجود دارد: annotated و lightweight. برای ساخت یک lightweight tag می‌توانید چیزی شبیه به این اجرا کنید:

$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d

این تمام چیزی است که یک lightweight tag هست — یک reference که هیچ‌وقت جابه‌جا نمی‌شود. اما یک annotated tag کمی پیچیده‌تر است. وقتی یک annotated tag می‌سازید، Git یک tag object ایجاد می‌کند و سپس یک reference می‌نویسد تا به آن اشاره کند، به‌جای اینکه مستقیماً به commit اشاره داشته باشد. می‌توانید این موضوع را با ساخت یک annotated tag (با استفاده از گزینه‌ی -a) ببینید:

$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'Test tag'

اینجا مقدار SHA-1 object ساخته‌شده را می‌بینید:

$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2

حالا دستور git cat-file -p را روی آن مقدار SHA-1 اجرا کنید:

$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700

Test tag

توجه کنید که این object entry به مقدار SHA-1 commit که شما تگ زده‌اید اشاره می‌کند. همچنین توجه داشته باشید که نیازی نیست حتماً به یک commit اشاره کند؛ شما می‌توانید هر Git object را تگ بزنید. برای مثال، در Git source code، نگه‌دارنده (maintainer) کلید عمومی GPG خودش را به‌عنوان یک blob object اضافه کرده و سپس آن را تگ زده است. می‌توانید کلید عمومی را با اجرای این دستور در یک clone از مخزن Git ببینید:

$ git cat-file blob junio-gpg-pub

مخزن Linux kernel نیز یک tag object دارد که به یک commit اشاره نمی‌کند — اولین tag ساخته‌شده به initial tree واردشده از source code اشاره دارد.

ریموت ها (Remotes)

سومین نوع reference که خواهید دید، یک remote reference است. اگر یک remote اضافه کنید و به آن push کنید، Git آخرین مقداری که به آن remote برای هر branch فرستاده‌اید را در مسیر refs/remotes ذخیره می‌کند. برای مثال، می‌توانید یک remote به نام origin اضافه کنید و branch master را به آن push کنید:

$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
  a11bef0..ca82a6d  master -> master

سپس می‌توانید ببینید که آخرین بار branch master روی remote origin چه بوده، با بررسی فایل refs/remotes/origin/master:

$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949

تفاوت اصلی remote references با branches (یعنی refs/heads) در این است که آن‌ها فقط-خواندنی (read-only) در نظر گرفته می‌شوند. شما می‌توانید به یکی از آن‌ها git checkout کنید، اما Git هرگز HEAD را به‌صورت سمبولیک به آن‌ها ارجاع نمی‌دهد، بنابراین هیچ‌وقت با دستور commit به‌روزرسانی نمی‌شوند. Git آن‌ها را مثل bookmark‌هایی مدیریت می‌کند که نشان‌دهنده آخرین وضعیت شناخته‌شده از آن branches روی آن servers هستند.

scroll-to-top