Git
Chapters ▾ 2nd Edition

7.12 Git инструменти - Пакети в Git (Bundling)

Пакети в Git (Bundling)

Вече разгледахме стандартните начини за трансфер на Git данни по мрежата (HTTP, SSH и т.н.), но има и още един начин да правим това, който е не толкова често използван, а може да бъде полезен.

Git може да “пакетира” своите данни в единичен файл. Това може да е ценно в различни ситуации. Може би мрежата ви е повредена, а искате да изпратите промените си до колегите. Може да работите някъде далеч офлайн и да нямате достъп до локалната офис мрежа. Дори може мрежовата ви карта да е повредена. Може да нямате достъп до споделен сървър за момента, искате да изпратите по имейла промени на някого, но не желаете да изпращате много къмити през format-patch.

Тук може да помогне командата git 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 като референция както направихме току що.

Можете да изпратите файла по имейл или да го копирате на флашка за когото е нужно.

Ако сте от другата страна и имате вече такъв файл, ето как да го използвате. Клонираме от бинарния файл в директория също както от 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

Първо, трябва да определим обхвата от къмити, които да включим в пакета. За разлика от случая с мрежовите протоколи, които определят това вместо нас, ще трябва да го подадем ръчно. Можете да направите същото нещо като в началото и да пакетирате цялото хранилище, това ще работи, но по-елегантно е да пакетирате само разликите, тоест само трите къмита направени локално.

За да направите това, ще трябва да калкулирате разликите. Както описахме в Обхвати от къмити, можете да указвате обхват от къмити по няколко начина. За да вземем само локалните три къмита, които липсват в оригинално клонирания клон, бихме могли да използваме нещо като 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

В нашия случай пакетът си е валиден, така че можем да извлечем къмитите от него. Ако искате да видите какви клонове съдържа пакета, също можете да го направите:

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