Skip to content

Latest commit

 

History

History
387 lines (323 loc) · 18.8 KB

File metadata and controls

387 lines (323 loc) · 18.8 KB

گیت و مرکوریال (Git and Mercurial)

جهان DVCS تنها محدود به گیت نیست. در واقع، سیستم‌های دیگری هم در این حوزه وجود دارند که هر کدام دیدگاه خود را دربارهٔ نحوهٔ درست انجام کنترل نسخهٔ توزیع‌شده دارند. علاوه بر گیت، محبوب‌ترینِ آنها مرکوریال است و این دو در بسیاری جهات بسیار شبیه هم‌اند.

خبر خوب این است که اگر رفتار سمت کلاینت گیت را می‌پسندید اما با پروژه‌ای سروکار دارید که کد منبعش با مرکوریال کنترل می‌شود، راهی هست که از گیت به‌عنوان کلاینت برای مخزن میزبانی‌شده با مرکوریال استفاده کنید. از آنجا که گیت از طریق remotes با مخازن سرور ارتباط برقرار می‌کند، تعجب‌آور نیست که این پل به‌صورت یک remote helper پیاده‌سازی شده است. نام این پروژه git-remote-hg است و می‌توانید آن را در https://github.com/felipec/git-remote-hg پیدا کنید.

ابزار گیت برای تعامل با Mercurial (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
شروع کار (Getting Started)

حال که یک مخزن «سمت سرور» مناسب داریم، می‌توانیم یک گردش‌کار معمولی را طی کنیم. همان‌طور که خواهید دید، این دو سیستم آن‌قدر به هم شبیه‌اند که اصطکاک چندانی وجود ندارد.

همانند همیشه در گیت، ابتدا 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 files

Git-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 عمل می‌کند، اما در کامیت‌ها گنجانده نمی‌شود.

گردش کار (Workflow)

فرض کنیم کمی کار کرده‌ایم و چند کامیت روی شاخه 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 ساخته شده بودند.

شاخه‌ها و بوک‌مارک‌ها (Branches and Bookmarks)

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" program

changesetهای ۸، ۹ و ۱۰ ایجاد شده و به شاخهٔ permanent تعلق دارند، اما changesetهای قدیمی همچنان وجود دارند. این می‌تواند برای هم‌تیمی‌های شما که از مرکوریال استفاده می‌کنند بسیار گیج‌کننده باشد، پس سعی کنید از این کار پرهیز کنید.

خلاصهٔ مرکوریال (Mercurial Summary)

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