جهان DVCS تنها محدود به گیت نیست. در واقع، سیستمهای دیگری هم در این حوزه وجود دارند که هر کدام دیدگاه خود را دربارهٔ نحوهٔ درست انجام کنترل نسخهٔ توزیعشده دارند. علاوه بر گیت، محبوبترینِ آنها مرکوریال است و این دو در بسیاری جهات بسیار شبیه هماند.
خبر خوب این است که اگر رفتار سمت کلاینت گیت را میپسندید اما با پروژهای سروکار دارید که کد منبعش با مرکوریال کنترل میشود، راهی هست که از گیت بهعنوان کلاینت برای مخزن میزبانیشده با مرکوریال استفاده کنید. از آنجا که گیت از طریق remotes با مخازن سرور ارتباط برقرار میکند، تعجبآور نیست که این پل بهصورت یک remote helper پیادهسازی شده است. نام این پروژه git-remote-hg است و میتوانید آن را در https://github.com/felipec/git-remote-hg پیدا کنید.
ابتدا باید git-remote-hg را نصب کنید. این در عمل به معنی قرار دادن فایل آن در مسیری است که در PATH شما قرار دارد، به این صورت:
$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg…assuming ~/bin is in your $PATH.
Git-remote-hg has one other dependency: the mercurial library for Python.
If you have Python installed, this is as simple as:
$ pip install mercurialاگر پایتون روی سیستمتان نصب نیست، به https://www.python.org/ مراجعه کرده و ابتدا آن را نصب کنید.
آخرین چیزی که نیاز دارید، کلاینت مرکوریال است. به https://www.mercurial-scm.org/ بروید و در صورت عدم نصب، آن را نصب کنید.
حالا آمادهاید. تنها چیزی که لازم دارید یک مخزن مرکوریال است که بتوانید به آن push کنید. خوشبختانه هر مخزن مرکوریال میتواند این نقش را ایفا کند، بنابراین ما از مخزن «سلام دنیا» که همه برای یادگیری مرکوریال از آن استفاده میکنند، استفاده میکنیم:
$ hg clone http://selenic.com/repo/hello /tmp/helloحال که یک مخزن «سمت سرور» مناسب داریم، میتوانیم یک گردشکار معمولی را طی کنیم.
همانطور که خواهید دید، این دو سیستم آنقدر به هم شبیهاند که اصطکاک چندانی وجود ندارد.
همانند همیشه در گیت، ابتدا clone میکنیم:
$ git clone hg::/tmp/hello /tmp/hello-git
$ cd /tmp/hello-git
$ git log --oneline --graph --decorate
* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile
* 65bb417 Create a standard 'hello, world' programمتوجه میشوید که کار با مخزن Mercurial از فرمان استاندارد `git clone` استفاده میکند. دلیلش این است که git-remote-hg در سطح نسبتاً پایینی کار میکند و از مکانیزمی مشابه با نحوه پیادهسازی پروتکل HTTP/S گیت (remote helpers) بهره میبرد. از آنجا که هم Git و هم Mercurial طوری طراحی شدهاند که هر کلاینت یک نسخه کامل از تاریخچهٔ مخزن داشته باشد، این فرمان یک کلون کامل میسازد که شامل تمام تاریخچهٔ پروژه است و این کار را نسبتاً سریع انجام میدهد.
فرمان log دو کامیت را نشان میدهد که جدیدترینِ آن توسط تعداد زیادی رِف اشاره شده است. معلوم میشود بعضی از اینها در واقع وجود خارجی ندارند. بیایید نگاهی به محتوای واقعی پوشهٔ .git بیندازیم:
$ tree .git/refs
.git/refs
├── heads
│ └── master
├── hg
│ └── origin
│ ├── bookmarks
│ │ └── master
│ └── branches
│ └── default
├── notes
│ └── hg
├── remotes
│ └── origin
│ └── HEAD
└── tags
9 directories, 5 filesGit-remote-hg تلاش میکند همهچیز را به شکلی آشنا برای گیت درآورد، اما در پشت صحنه نگاشت مفهومی بین دو سیستم کمی متفاوت را مدیریت میکند. پوشهٔ refs/hg جایی است که رِفهای واقعیِ راه دور در آن ذخیره میشوند. برای مثال، refs/hg/origin/branches/default یک فایل رِف گیت است که شامل SHA-1ای است که با "ac7955c" شروع میشود و همان کامیتی است که master به آن اشاره میکند. بنابراین پوشهٔ refs/hg نوعی مثلِ یک refs/remotes/origin ساختگی است، اما تمایز بین bookmarks و branches را نیز حفظ میکند.
فایل notes/hg نقطهٔ شروعِ نحوهٔ نگاشت هشهای کامیت گیت به شناسههای changeset در Mercurial است. بیایید کمی بررسی کنیم:
$ cat notes/hg
d4c10386...
$ git cat-file -p d4c10386...
tree 1781c96...
author remote-hg <> 1408066400 -0800
committer remote-hg <> 1408066400 -0800
Notes for master
$ git ls-tree 1781c96...
100644 blob ac9117f... 65bb417...
100644 blob 485e178... ac7955c...
$ git cat-file -p ac9117f
0a04b987be5ae354b710cefeba0e2d9de7ad41a9پس refs/notes/hg به یک درخت اشاره میکند که در پایگاه دادهٔ اشیاء گیت فهرستی از اشیاء دیگر با نامهاست. git ls-tree مد، نوع، هش شیء و نام فایل آیتمهای داخل یک درخت را نمایش میدهد. وقتی عمیقتر به یکی از آیتمهای درخت میرویم، میبینیم که داخل آن یک blob به نام "ac9117f" (هش SHA-1ِ کامیتی که master به آن اشاره میکند) وجود دارد که محتوای آن "0a04b98" است (که شناسهٔ changesetِ Mercurial در رأسِ شاخهٔ default است).
خبر خوب این است که بیشتر اوقات لازم نیست نگران همهٔ این جزئیات باشیم. جریان کار معمولی چندان با کار کردن با یک راه دور گیت تفاوتی نخواهد داشت.
یک چیز دیگر هست که قبل از ادامه باید به آن رسیدگی کنیم: فایلهای ignore. Mercurial و Git مکانیزم بسیار مشابهی برای این کار دارند، ولی احتمالاً شما نمیخواهید فایل `.gitignore` را در یک مخزن Mercurial کامیت کنید. خوشبختانه Git راهی برای نادیده گرفتن فایلها دارد که محلی برای مخزن روی دیسک است، و فرمت Mercurial با Git سازگار است، پس کافی است آن را کپی کنید:
$ cp .hgignore .git/info/excludeفایل .git/info/exclude دقیقاً مانند یک .gitignore عمل میکند، اما در کامیتها گنجانده نمیشود.
فرض کنیم کمی کار کردهایم و چند کامیت روی شاخه master ساختهایم و حالا آمادهایم که آن را به مخزن راه دور پوش کنیم.
در حال حاضر مخزن ما اینطور به نظر میرسد:
$ git log --oneline --graph --decorate
* ba04a2a (HEAD, master) Update makefile
* d25d16f Goodbye
* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile
* 65bb417 Create a standard 'hello, world' programشاخه master ما دو کامیت جلوتر از origin/master است، اما آن دو کامیت فقط روی ماشین محلی ما وجود دارند.
باید ببینیم آیا کس دیگری هم همزمان کار مهمی انجام داده است یا نه:
$ git fetch
From hg::/tmp/hello
ac7955c..df85e87 master -> origin/master
ac7955c..df85e87 branches/default -> origin/branches/default
$ git log --oneline --graph --decorate --all
* 7b07969 (refs/notes/hg) Notes for default
* d4c1038 Notes for master
* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
| * ba04a2a (HEAD, master) Update makefile
| * d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' programاز آنجا که از فلگ --all استفاده کردیم، رفرنسهای "notes" که بهصورت داخلی توسط git-remote-hg استفاده میشوند را میبینیم، اما میتوانیم آنها را نادیده بگیریم.
بقیه همان چیزی است که انتظار داشتیم؛ origin/master یک کامیت جلو رفته و تاریخچه ما اکنون منشعب شده است.
برخلاف سیستمهای دیگر که در این فصل کار میکنیم، Mercurial قادر به مدیریت mergeها است، پس قرار نیست کار خاصی پیچیده انجام دهیم.
$ git merge origin/master
Auto-merging hello.c
Merge made by the 'recursive' strategy.
hello.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --decorate
* 0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\
| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
* | ba04a2a Update makefile
* | d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' programعالی. تستها را اجرا میکنیم و همه چیز پاس میشود، پس آمادهایم کارمان را با بقیه تیم بهاشتراک بگذاریم:
$ git push
To hg::/tmp/hello
df85e87..0c64627 master -> masterهمین بود! اگر به مخزن Mercurial نگاهی بیندازید، خواهید دید که این همان چیزی را انجام داده که انتظار داشتیم:
$ hg log -G --style compact
o 5[tip]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben
| | Update makefile
| |
| o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben
| | Goodbye
| |
@ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' programچِنجست شماره 2 توسط Mercurial ساخته شده بود، و چِنجستهای شمارههای 3 و 4 توسط git-remote-hg ساخته شدهاند، با پوش کردن کامیتهایی که با Git ساخته شده بودند.
Git فقط یک نوع شاخه دارد: یک ارجاع که هنگام ساختن کامیتها جابجا میشود. در Mercurial، این نوع ارجاع «بوکمارک» (bookmark) نامیده میشود و رفتار آن تا حد زیادی شبیه شاخه در Git است.
مفهوم «شاخه» در مرکوریال سنگینتر است. شاخهای که یک changeset روی آن ساخته شده است همراهِ خود changeset ثبت میشود؛ یعنی همیشه در تاریخچهٔ مخزن خواهد ماند. در اینجا مثالی از یک commit را میبینید که روی شاخهٔ develop ساخته شده است:
$ hg log -l 1
changeset: 6:8f65e5e02793
branch: develop
tag: tip
user: Ben Straub <ben@straub.cc>
date: Thu Aug 14 20:06:38 2014 -0700
summary: More documentationخطی را که با «branch» شروع میشود، توجه کنید. گیت واقعاً نمیتواند این را بازتولید کند (و نیازی هم ندارد؛ هر دو نوع شاخه را میتوان بهصورت یک ref گیت نمایش داد)، اما git-remote-hg باید این تمایز را بفهمد، چون مرکوریال برایش اهمیت دارد.
ایجاد bookmarks در مرکوریال به همان سادگی ایجاد شاخههای گیت است. در سمت گیت:
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
* [new branch] featureA -> featureAهمین بود. در سمت مرکوریال، شبیه این به نظر میرسد:
$ hg bookmarks
featureA 5:bd5ac26f11f9
$ hg log --style compact -G
@ 6[tip] 8f65e5e02793 2014-08-14 20:06 -0700 ben
| More documentation
|
o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | update makefile
| |
| o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' programبرچسب جدید [featureA] روی بازبینی ۵ را مشاهده کنید.
اینها در سمت گیت دقیقاً مثل شاخههای گیت عمل میکنند، با یک استثنا: شما نمیتوانید یک bookmark را از سمت گیت حذف کنید (این محدودیتی از طرف remote helperها است).
شما همچنین میتوانید روی یک شاخهٔ «سنگینوزن» مرکوریال کار کنید: فقط یک شاخه را در فضای نام branches قرار دهید:
$ git checkout -b branches/permanent
Switched to a new branch 'branches/permanent'
$ vi Makefile
$ git commit -am 'A permanent change'
$ git push origin branches/permanent
To hg::/tmp/hello
* [new branch] branches/permanent -> branches/permanentدر سمت مرکوریال اینطور دیده میشود:
$ hg branches
permanent 7:a4529d07aad4
develop 6:8f65e5e02793
default 5:bd5ac26f11f9 (inactive)
$ hg log -G
o changeset: 7:a4529d07aad4
| branch: permanent
| tag: tip
| parent: 5:bd5ac26f11f9
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:06:38 2014 -0700
| summary: More documentation
|
o changeset: 5:bd5ac26f11f9
|\ bookmark: featureA
| | parent: 4:0434aaa6b91f
| | parent: 2:f098c7f45c4f
| | user: Ben Straub <ben@straub.cc>
| | date: Thu Aug 14 20:02:21 2014 -0700
| | summary: Merge remote-tracking branch 'origin/master'
[...]نام شاخهٔ «permanent» همراه با changeset شمارهٔ ۷ ثبت شده بود.
از سمت گیت، کار کردن با هر یک از این سبکهای شاخه یکسان است: کافی است مثل همیشه checkout، commit، fetch، merge، pull و push کنید. یک نکته که باید بدانید این است که مرکوریال بازنویسی تاریخچه را پشتیبانی نمیکند و تنها افزودن به آن را ممکن میسازد. این تصویری است از مخزن مرکوریال ما پس از یک rebase تعاملی و یک force-push:
$ hg log --style compact -G
o 10[tip] 99611176cbc9 2014-08-14 20:21 -0700 ben
| A permanent change
|
o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben
| Add some documentation
|
o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben
| goodbye
|
| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben
| | A permanent change
| |
| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben
| |/ More documentation
| |
| o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
| |\ Merge remote-tracking branch 'origin/master'
| | |
| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | | update makefile
| | |
+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
| o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" programchangesetهای ۸، ۹ و ۱۰ ایجاد شده و به شاخهٔ permanent تعلق دارند، اما changesetهای قدیمی همچنان وجود دارند. این میتواند برای همتیمیهای شما که از مرکوریال استفاده میکنند بسیار گیجکننده باشد، پس سعی کنید از این کار پرهیز کنید.