اگرچه روشهای معمول انتقال دادههای Git از طریق شبکه (HTTP، SSH و غیره) را بررسی کردهایم، اما در واقع یک روش دیگر هم برای این کار وجود دارد که معمولاً کمتر استفاده میشود اما میتواند بسیار کاربردی باشد.
Git قادر است دادههای خود را در قالب یک فایل واحد «بستهبندی» (bundle) کند. این قابلیت در موقعیتهای مختلفی مفید است. شاید شبکه شما قطع شده و میخواهید تغییرات را به همکارانتان ارسال کنید. شاید در مکانی خارج از محل کار هستید و به دلایل امنیتی به شبکه محلی دسترسی ندارید. ممکن است کارت بیسیم یا اترنت شما خراب شده باشد. یا شاید در حال حاضر به سرور مشترک دسترسی ندارید و میخواهید به کسی بهروزرسانیها را ایمیل کنید بدون اینکه بخواهید ۴۰ کامیت را از طریق format-patch ارسال کنید.
در اینجا دستور git bundle میتواند به کمک شما بیاید. دستور bundle همه چیزهایی را که معمولاً با دستور git push روی شبکه ارسال میشود، در قالب یک فایل باینری بستهبندی میکند که میتوانید آن را برای کسی ایمیل کنید یا روی یک فلشدرایو بگذارید و سپس در مخزن دیگری از حالت بسته خارج (unbundle) کنید.
بیایید یک مثال ساده ببینیم. فرض کنید مخزنی با دو کامیت دارید:
$ git log
commit 9a466c572fe88b195efd356c3f2bbeccdb504102
Author: Scott Chacon <schacon@gmail.com>
Date: Wed Mar 10 07:34:10 2010 -0800
Second commit
commit b1ec3248f39900d2a406049d762aa68e9641be25
Author: Scott Chacon <schacon@gmail.com>
Date: Wed Mar 10 07:34:01 2010 -0800
First commitاگر بخواهید این مخزن را برای کسی بفرستید و دسترسی به مخزنی برای push کردن ندارید، یا اصلاً نمیخواهید یک مخزن تنظیم کنید، میتوانید با دستور git bundle create آن را بستهبندی کنید.
$ git bundle create repo.bundle HEAD master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 441 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)حالا یک فایل به نام repo.bundle دارید که همه دادههای لازم برای بازسازی شاخه master مخزن را در خود دارد. هنگام استفاده از دستور bundle باید همه مرجعها (references) یا بازههای مشخصی از کامیتها که میخواهید در بسته قرار بگیرند را لیست کنید. اگر قصد دارید این بسته در جای دیگری کلون شود، باید HEAD را هم به عنوان مرجع اضافه کنید، همانطور که اینجا انجام دادیم.
میتوانید فایل repo.bundle را برای شخص دیگری ایمیل کنید یا روی یک فلشدرایو کپی کرده و به او تحویل دهید.
طرف مقابل، وقتی این فایل repo.bundle را دریافت کرد و خواست روی پروژه کار کند، میتواند مانند کلون کردن از یک URL، از روی این فایل باینری کلون کند.
$ git clone repo.bundle repo
Cloning into 'repo'...
...
$ cd repo
$ git log --oneline
9a466c5 Second commit
b1ec324 First commitاگر HEAD را در مراجع وارد نکنید، باید با گزینه -b master یا هر شاخهای که شامل آن است، مشخص کنید که چه شاخهای باید چکاوت شود، وگرنه Git نمیداند کدام شاخه را باید باز کند.
حالا فرض کنیم سه کامیت جدید روی آن انجام دادید و میخواهید این کامیتهای جدید را از طریق بستهبندی روی فلشدرایو یا ایمیل ارسال کنید.
$ git log --oneline
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo
9a466c5 Second commit
b1ec324 First commitابتدا باید بازه کامیتهایی که میخواهید در بسته قرار دهید را مشخص کنیم. برخلاف پروتکلهای شبکه که حداقل داده موردنیاز برای انتقال را به طور اتوماتیک تشخیص میدهند، اینجا باید خودمان این کار را انجام دهیم. البته میتوانید کل مخزن را بستهبندی کنید که جواب میدهد، اما بهتر است فقط تفاوتها یعنی همان سه کامیتی که به صورت محلی اضافه کردهاید را بستهبندی کنید.
برای این کار باید تفاوت را محاسبه کنید. همانطور که در ch07-git-tools.asc توضیح دادهایم، میتوانید بازه کامیتها را به روشهای مختلف مشخص کنید. برای گرفتن سه کامیتی که در شاخه master داریم و در شاخهای که ابتدا کلون کردهایم نبود، میتوانیم از عباراتی مانند origin/master..master یا master ^origin/master استفاده کنیم. میتوانید این را با دستور log آزمایش کنید.
$ git log --oneline master ^origin/master
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repoحالا که لیست کامیتهایی که میخواهیم در بسته قرار دهیم را داریم، آنها را بستهبندی میکنیم. این کار را با دستور git bundle create انجام میدهیم، به آن نام فایلی که میخواهیم بسته ما داشته باشد و بازه کامیتها را میدهیم.
$ git bundle create commits.bundle master ^9a466c5
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 775 bytes, done.
Total 9 (delta 0), reused 0 (delta 0)حالا فایل commits.bundle در دایرکتوری ما ایجاد شده است. اگر این فایل را برای همکارتان ارسال کنیم، او میتواند آن را در مخزن اصلی وارد کند، حتی اگر در این فاصله کارهای بیشتری روی مخزن انجام شده باشد.
وقتی او بسته را دریافت کرد، میتواند قبل از وارد کردن آن به مخزنش، محتویات آن را بررسی کند. اولین دستور، bundle verify است که مطمئن میشود فایل واقعاً یک بسته معتبر Git است و همه پیشینههای لازم برای بازسازی آن را دارید.
$ git bundle verify ../commits.bundle
The bundle contains 1 ref
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
The bundle requires these 1 ref
9a466c572fe88b195efd356c3f2bbeccdb504102 second commit
../commits.bundle is okayاگر بستهبند فقط آخرین دو کامیتی که انجام داده بود را بستهبندی کرده بود به جای هر سه، مخزن اصلی قادر به وارد کردن آن نبود چون تاریخچه لازم ناقص بود. دستور verify به این صورت بود:
$ git bundle verify ../commits-bad.bundle
error: Repository lacks these prerequisite commits:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 Third commit - second repoاما بسته اول ما معتبر است، پس میتوانیم کامیتها را از آن دریافت کنیم. اگر بخواهید ببینید چه شاخههایی در بسته وجود دارد که میتوان وارد کرد، دستور جداگانهای برای لیست کردن سرشاخهها (heads) وجود دارد:
$ git bundle list-heads ../commits.bundle
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/masterدستور verify هم به شما سرشاخهها را نشان میدهد. هدف این است که ببینید چه چیزی قابل دریافت است، تا بتوانید از دستورات fetch یا pull برای وارد کردن کامیتها از این بسته استفاده کنید. در اینجا شاخه master بسته را به شاخهای به نام other-master در مخزن خود fetch میکنیم:
$ git fetch ../commits.bundle master:other-master
From ../commits.bundle
* [new branch] master -> other-masterحالا میبینیم که کامیتهای وارد شده در شاخه other-master وجود دارند و همچنین هر کامیتی که در همین فاصله روی شاخه master خودمان انجام دادهایم.
$ git log --oneline --decorate --graph --all
* 8255d41 (HEAD, master) Third commit - first repo
| * 71b84da (other-master) Last commit - second repo
| * c99cf5b Fourth commit - second repo
| * 7011d3d Third commit - second repo
|/
* 9a466c5 Second commit
* b1ec324 First commitپس، دستور git bundle میتواند بسیار مفید باشد برای به اشتراکگذاری یا انجام عملیات مشابه شبکه وقتی که دسترسی به شبکه یا مخزن مشترک مناسب ندارید.