Git
Chapters ▾ 2nd Edition

7.12 Інструменти Git - Пакування

Пакування

Хоч ми й розглянули звичайні способи передачі даних через мережу (HTTP, SSH тощо), насправді існує ще один, не такий поширений метод зробити це, що іноді може бути доволі корисним.

Git здатен до “пакування” (bundling) своїх даних в один файл. Це може бути корисним у різноманітних випадках. Можливо, ваша мережа не працює, а ви бажаєте відправити свої зміни співробітникам. Мабуть, ви працюєте десь поза офісом та не маєте доступу до локальної мережі через заходи безпеки. Можливо, ваша мережева картка просто зламалась. Можливо, наразі у вас немає доступу до спільного сервера, ви бажаєте надіслати електронного листа з оновленнями, проте не бажаєте відправляти 40 комітів через format-patch.

Ось тоді команда git bundle може допомогти. Команда bundle спакує все, що зазвичай передається через дріт командою git push, у двійковий файл, який ви можете передати поштою або записати на флеш-накопичувач, а потім розпакувати його в інший репозиторій.

Розгляньмо простий приклад. Скажімо, у вас є репозиторій з двома комітами:

$ 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

Якщо ви бажаєте відправити цей репозиторій комусь, проте не маєте туди доступу на запис, або просто не бажаєте налаштовувати доступ, ви можете спакувати його за допомогою 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 кожне посилання або низку комітів, які ви бажаєте включити. Якщо ви збираєтесь таким чином створювати клон, то маєте також додати до посилань HEAD, як ми тут зробили.

Ви можете відправити файл repo.bundle електронною поштою комусь, або скопіювати його на USB накопичувач та віднести кудись.

З іншого боку, припустимо, що вам надіслали цей файл 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 не знатиме на яку гілку переключатись.

Тепер, припустімо, що ви створюєте три коміти у цій гілці та бажаєте відправити нові коміти назад за допомогою пакунку через електронну пошту або за допомогою USB накопичувача.

$ git log --oneline
71b84da last commit - second repo
c99cf5b fourth commit - second repo
7011d3d third commit - second repo
9a466c5 second commit
b1ec324 first commit

Спочатку треба визначити, які саме коміти ми бажаємо включити в пакунок. На відміну від мережевих протоколів, які самі можуть знайти мінімальний набір даних для передачі через мережу, нам доведеться це зробити самотужки. Тепер, ви могли би просто зробити те саме та запакувати весь репозиторій, і це спрацює, проте краще спакувати лише різницю - лише три коміти, які ми щойно створили локально.

Щоб це зробити, вам треба обчислити різницю. Як було описано в Інтервали комітів, ви можете задати низку комітів безліччю методів. Щоб отримати три коміти, які є в гілці 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:

$ 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 може бути дійсно корисним, щоб надати комусь зміни або робити мережеві операції, коли у вас немає відповідної мережі або спільного репозиторія, щоб це робити.