Chapters ▾ 2nd Edition

3.5 انشعاب‌گیری در گیت (Git Branching) - شاخه‌های راه دور (Remote Branches)

شاخه‌های راه دور (Remote Branches)

ارجاعات راه دور، ارجاعاتی (اشاره‌گرها) در مخازن راه دور شما هستند، شامل شاخه‌ها، برچسب‌ها و غیره. شما می‌توانید فهرست کامل ارجاعات راه دور را به‌طور صریح با دستور git ls-remote <remote> یا برای شاخه‌های راه دور و همچنین اطلاعات بیشتر با دستور git remote show <remote> دریافت کنید. با این حال، روش رایج‌تر استفاده از شاخه‌های پیگیری راه دور است.

شاخه‌های پیگیری راه دور، ارجاعاتی به وضعیت شاخه‌های راه دور هستند. این‌ها ارجاعات محلی‌اند که شما نمی‌توانید آن‌ها را جابه‌جا کنید؛ گیت هر بار که ارتباط شبکه‌ای برقرار می‌کنید، آن‌ها را برای شما به‌روزرسانی می‌کند تا مطمئن شود که دقیقاً وضعیت مخزن راه دور را نشان می‌دهند. می‌توانید آن‌ها را مانند بوکمارک‌هایی فرض کنید که به شما یادآوری می‌کنند شاخه‌های مخازن راه دور شما آخرین بار که به آن‌ها متصل شده‌اید، در چه وضعیتی بوده‌اند.

نام شاخه‌های پیگیری راه دور به شکل <remote>/<branch> است. برای مثال، اگر بخواهید ببینید شاخه master روی راه دور origin آخرین بار که ارتباط برقرار کرده‌اید چگونه بوده، باید شاخه origin/master را بررسی کنید. اگر روی یک مسئله با همکار خود کار می‌کنید و او شاخه‌ای به نام iss53 روی سرور منتشر کرده است، ممکن است شما شاخه محلی iss53 خود را داشته باشید، اما شاخه‌ای که روی سرور است با شاخه پیگیری راه دور origin/iss53 نشان داده می‌شود.

این ممکن است کمی گیج‌کننده باشد، پس بیایید یک مثال ببینیم. فرض کنید شما یک سرور گیت در شبکه خود دارید به آدرس git.ourcompany.com. اگر از این سرور کلون بگیرید، دستور git clone به‌طور خودکار آن را origin نام‌گذاری می‌کند، تمام داده‌های آن را دانلود می‌کند، اشاره‌گری به جایگاه شاخه master آن ایجاد می‌کند و به‌صورت محلی آن را origin/master می‌نامد. گیت همچنین یک شاخه محلی master برای شما ایجاد می‌کند که از همان نقطه شاخه master در origin شروع می‌شود، تا شما چیزی برای کار کردن داشته باشید.

یادداشت
“origin” is not special

درست مانند اینکه نام شاخه “master” در گیت معنای خاصی ندارد، نام “origin” هم چنین نیست. در حالی که “master” نام پیش‌فرض شاخه شروعی است وقتی git init اجرا می‌کنید و به همین دلیل بسیار استفاده می‌شود، “origin” نام پیش‌فرض راه دور است وقتی git clone اجرا می‌کنید. اگر دستور git clone -o booyah را بدهید، شاخه پیش‌فرض راه دور شما به جای origin/master، booyah/master خواهد بود.

Server and local repositories after cloning
نمودار 30. Server and local repositories after cloning

اگر روی شاخه محلی master خود کار کنید و در همین حین شخص دیگری روی سرور git.ourcompany.com شاخه master را به‌روزرسانی کند، تاریخچه‌های شما به شکل متفاوتی پیش می‌روند. همچنین تا زمانی که با سرور origin ارتباط برقرار نکنید، اشاره‌گر origin/master جابه‌جا نمی‌شود.

Local and remote work can diverge
نمودار 31. Local and remote work can diverge
 برای همگام‌سازی کار خود با یک ریموت مشخص، فرمان `git fetch <remote>` را اجرا می‌کنید (در مورد ما، `git fetch origin`).
این فرمان سروری که "`origin`" نامیده شده را شناسایی می‌کند (در اینجا، `git.ourcompany.com` است)، هر داده‌ای را که هنوز ندارید از آن دریافت می‌کند و پایگاه داده محلی شما را به‌روزرسانی می‌کند و اشاره‌گر `origin/master` را به موقعیت جدید و به‌روزتر منتقل می‌نماید.
`git fetch` updates your remote-tracking branches
نمودار 32. git fetch updates your remote-tracking branches

برای نشان دادن داشتن چند سرور ریموت و مشاهده شاخه‌های ریموت برای آن پروژه‌ها، فرض کنیم سرور Git داخلی دیگری دارید که فقط توسط یکی از تیم‌های اسپرینت شما برای توسعه استفاده می‌شود. این سرور در آدرس git.team1.ourcompany.com قرار دارد. شما می‌توانید آن را به عنوان یک مرجع ریموت جدید به پروژه‌ای که در حال حاضر روی آن کار می‌کنید اضافه کنید، با اجرای فرمان git remote add که در مقدمات گیت (git basics chapter) توضیح داده شده است. این ریموت را teamone نام‌گذاری کنید، که نام کوتاه شما برای آن URL خواهد بود.

Adding another server as a remote
نمودار 33. Adding another server as a remote

حالا می‌توانید با اجرای git fetch teamone همه چیزهایی را که سرور ریموت teamone دارد و شما هنوز ندارید دریافت کنید. از آنجا که این سرور زیرمجموعه‌ای از داده‌هایی را که سرور origin شما دارد، در اختیار دارد، گیت داده‌ای دریافت نمی‌کند اما شاخه‌ای به نام teamone/master که شاخه ریموت‌ترکینگ است را به تعهدی که teamone به عنوان شاخه master خود دارد، اشاره می‌دهد.

Remote-tracking branch for `teamone/master`
نمودار 34. Remote-tracking branch for teamone/master

ارسال (Pushing)

وقتی می‌خواهید یک شاخه را با دیگران به اشتراک بگذارید، باید آن را به یک ریموتی که دسترسی نوشتن دارید ارسال کنید. شاخه‌های محلی شما به صورت خودکار با ریموت‌هایی که روی آن‌ها می‌نویسید همگام نمی‌شوند — شما باید به طور صریح شاخه‌هایی را که می‌خواهید به اشتراک بگذارید ارسال (push) کنید. به این ترتیب، می‌توانید از شاخه‌های خصوصی برای کارهایی که نمی‌خواهید به اشتراک بگذارید استفاده کنید و تنها شاخه‌های موضوعی که می‌خواهید روی آن‌ها همکاری کنید را ارسال نمایید.

اگر شاخه‌ای به نام serverfix دارید که می‌خواهید با دیگران روی آن کار کنید، می‌توانید آن را به همان روشی که شاخه اول خود را ارسال کردید، ارسال کنید. فرمان git push <remote> <branch> را اجرا کنید:

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix

این روش کمی کوتاه شده است. گیت به طور خودکار نام شاخه serverfix را به refs/heads/serverfix:refs/heads/serverfix گسترش می‌دهد، که یعنی: «شاخه محلی serverfix من را بگیر و شاخه serverfix ریموت را به‌روزرسانی کن.» ما بخش refs/heads/ را به طور کامل در (Git Internals) بررسی خواهیم کرد، اما معمولاً می‌توانید آن را نادیده بگیرید. همچنین می‌توانید از git push origin serverfix:serverfix استفاده کنید که همان کار را انجام می‌دهد — یعنی «شاخه serverfix من را بگیر و آن را به شاخه serverfix ریموت تبدیل کن.» می‌توانید از این قالب برای ارسال یک شاخه محلی به شاخه‌ای در ریموت با نام متفاوت استفاده کنید. اگر نمی‌خواستید شاخه ریموت به نام serverfix باشد، می‌توانستید به جای آن این فرمان را اجرا کنید: git push origin serverfix:awesomebranch تا شاخه محلی serverfix را به شاخه awesomebranch در پروژه ریموت ارسال کنید.

یادداشت
Don’t type your password every time

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

اگر نمی‌خواهید هر بار که ارسال می‌کنید این اطلاعات را تایپ کنید، می‌توانید یک «کش اعتبارسنجی» (credential cache) تنظیم کنید. ساده‌ترین روش این است که اطلاعات را چند دقیقه‌ای در حافظه نگه دارد که با اجرای فرمان git config --global credential.helper cache به راحتی قابل تنظیم است.

برای اطلاعات بیشتر درباره گزینه‌های مختلف کش اعتبارسنجی، به ذخیره‌سازی اطلاعات ورود (Credential Storage) مراجعه کنید.

بار بعد که یکی از همکارانتان از سرور داده‌ها را دریافت کند، ارجاعی به این که نسخه سرور از شاخه serverfix کجا قرار دارد، تحت شاخه ریموت origin/serverfix دریافت خواهد کرد:

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix

مهم است بدانید که وقتی دستوری مانند fetch اجرا می‌کنید که شاخه‌های جدید remote-tracking را دریافت می‌کند، به‌طور خودکار نسخه‌های محلی و قابل ویرایش از آن‌ها ندارید. به عبارت دیگر، در این حالت، شما شاخه‌ی جدیدی به نام serverfix ندارید — بلکه فقط یک اشاره‌گر origin/serverfix دارید که نمی‌توانید آن را تغییر دهید.

برای ادغام این تغییرات در شاخه‌ی کاری فعلی خود، می‌توانید دستور git merge origin/serverfix را اجرا کنید. اگر می‌خواهید شاخه‌ی محلی serverfix خود را داشته باشید که بتوانید روی آن کار کنید، می‌توانید آن را بر اساس شاخه‌ی remote-tracking خود بسازید:

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

این کار یک شاخه‌ی محلی برای شما ایجاد می‌کند که می‌توانید روی آن کار کنید و شروع آن دقیقاً همان جایی است که origin/serverfix قرار دارد.

شاخه‌های دنبال‌کننده (Tracking Branches)

وقتی یک شاخه‌ی محلی را از روی یک شاخه‌ی remote-tracking برمی‌دارید، به‌طور خودکار چیزی به نام «شاخه‌ی پیگیری» (tracking branch) ایجاد می‌شود (و شاخه‌ای که پیگیری می‌کند، «شاخه‌ی بالادستی» یا upstream branch نامیده می‌شود). شاخه‌های پیگیری، شاخه‌های محلی‌ای هستند که رابطه‌ی مستقیمی با یک شاخه‌ی ریموت دارند. اگر روی یک شاخه‌ی پیگیری باشید و دستور git pull را بزنید، گیت به‌طور خودکار می‌داند که از کدام سرور باید دریافت کند و کدام شاخه را باید ادغام کند.

وقتی یک مخزن را کلون می‌کنید، معمولاً به‌طور خودکار شاخه‌ی master ایجاد می‌شود که شاخه‌ی origin/master را پیگیری می‌کند. با این حال، می‌توانید شاخه‌های پیگیری دیگری هم بسازید — شاخه‌هایی که شاخه‌های ریموت دیگری را پیگیری می‌کنند، یا شاخه‌ی master را پیگیری نمی‌کنند. حالت ساده همان مثالی است که دیدید: اجرای دستور git checkout -b <branch> <remote>/<branch>. این عملیات آن‌قدر رایج است که گیت میانبر --track را ارائه کرده است:

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

در واقع، این آن‌قدر رایج است که حتی یک میانبر برای همان میانبر وجود دارد. اگر شاخه‌ای که می‌خواهید چک‌اوت کنید (الف) وجود نداشته باشد و (ب) دقیقاً با نام یک شاخه روی تنها یک ریموت مطابقت داشته باشد، گیت به‌طور خودکار یک شاخه‌ی پیگیری برای شما ایجاد می‌کند:

$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

برای ساخت یک شاخه‌ی محلی با نامی متفاوت از شاخه‌ی ریموت، می‌توانید به‌راحتی از نسخه‌ی اول با نام شاخه‌ی محلی متفاوت استفاده کنید:

$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'

حالا شاخه‌ی محلی شما به نام sf به‌طور خودکار از origin/serverfix دریافت خواهد کرد.

اگر از قبل یک شاخه‌ی محلی دارید و می‌خواهید آن را به شاخه‌ی ریموتی که تازه دریافت کرده‌اید مرتبط کنید، یا می‌خواهید شاخه‌ی بالادستی (upstream) که پیگیری می‌کنید را تغییر دهید، می‌توانید در هر زمان با گزینه‌های -u یا --set-upstream-to دستور git branch این کار را به‌طور صریح انجام دهید.

$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
یادداشت
Upstream shorthand
 وقتی یک شاخه رهگیری (tracking branch) تنظیم کرده باشید، می‌توانید شاخه بالادستی آن را با کوتاه‌شده‌های `@{upstream}` یا `@{u}` اشاره کنید.
پس اگر روی شاخه `master` باشید و این شاخه در حال رهگیری `origin/master` باشد، می‌توانید به جای نوشتن `git merge origin/master` بنویسید `git merge @{u}`.(((@{u})))(((@{upstream})))

اگر بخواهید ببینید چه شاخه‌های پیگیری تنظیم کرده‌اید، می‌توانید از گزینه‌ی -vv دستور git branch استفاده کنید. این دستور شاخه‌های محلی شما را همراه با اطلاعات بیشتر، از جمله شاخه‌ای که هر کدام پیگیری می‌کند و اینکه شاخه‌ی محلی جلوتر، عقب‌تر یا هر دو است، نمایش می‌دهد.

$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] Add forgotten brackets
  master    1ae2a45 [origin/master] Deploy index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] This should do it
  testing   5ea463a Try something new

در اینجا می‌توانیم ببینیم که شاخه‌ی iss53 ما شاخه‌ی origin/iss53 را پیگیری می‌کند و «جلو» است به اندازه‌ی دو، یعنی دو کامیت محلی داریم که هنوز به سرور ارسال نشده‌اند. همچنین می‌بینیم که شاخه‌ی master ما شاخه‌ی origin/master را پیگیری می‌کند و به‌روز است. بعد می‌بینیم که شاخه‌ی serverfix شاخه‌ی server-fix-good روی سرور teamone را پیگیری می‌کند و سه کامیت جلو و یک کامیت عقب است، یعنی یک کامیت روی سرور است که هنوز ادغام نکرده‌ایم و سه کامیت محلی داریم که هنوز ارسال نکرده‌ایم. در نهایت، می‌بینیم که شاخه‌ی testing هیچ شاخه‌ی ریموتی را پیگیری نمی‌کند.

مهم است بدانید که این اعداد فقط از آخرین باری که از هر سرور fetch کرده‌اید به روز است. این دستور به سرورها متصل نمی‌شود، بلکه اطلاعات کش شده از این سرورها را به شما نشان می‌دهد. اگر بخواهید اعداد جلو یا عقب کاملاً به‌روز باشند، باید درست قبل از اجرای این دستور، از همه‌ی ریموت‌های خود fetch کنید. می‌توانید این کار را این‌گونه انجام دهید:

$ git fetch --all; git branch -vv

Pulling (دریافت کردن)

دستور git fetch تمام تغییراتی را که روی سرور وجود دارد و شما هنوز ندارید، دریافت می‌کند، اما اصلاً دایرکتوری کاری شما را تغییر نمی‌دهد. این دستور فقط داده‌ها را برای شما می‌گیرد و اجازه می‌دهد خودتان آنها را ادغام کنید. با این حال، دستوری به نام git pull وجود دارد که در واقع همان git fetch است که بلافاصله در بیشتر موارد با یک git merge دنبال می‌شود. اگر شاخه رهگیری تنظیم کرده باشید، مثل آنچه در بخش قبل توضیح داده شد، چه به صورت دستی تنظیم شده باشد یا به صورت خودکار توسط دستورات clone یا checkout ایجاد شده باشد، دستور git pull بررسی می‌کند که شاخه فعلی شما در حال رهگیری کدام سرور و شاخه است، از آن سرور دریافت می‌کند و سپس تلاش می‌کند آن شاخه راه دور را ادغام کند.

حذف شاخه ها (Deleting Remote Branches)

فرض کنید دیگر به یک شاخه راه دور نیازی ندارید — مثلاً شما و همکارانتان کار روی یک ویژگی را تمام کرده‌اید و آن را به شاخه master راه دور (یا هر شاخه‌ای که خط پایدار کد شما در آن است) ادغام کرده‌اید. می‌توانید یک شاخه راه دور را با گزینه --delete در دستور git push حذف کنید. اگر می‌خواهید شاخه serverfix را از سرور حذف کنید، دستور زیر را اجرا می‌کنید:

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

در اصل کاری که انجام می‌دهد این است که اشاره‌گر شاخه را از سرور حذف می‌کند. سرور گیت معمولاً داده‌ها را برای مدتی نگه می‌دارد تا زمانی که عملیات جمع‌آوری زباله انجام شود، بنابراین اگر به اشتباه حذف شده باشد، اغلب بازیابی آن آسان است.

scroll-to-top