Chapters ▾ 2nd Edition

7.12 Ferramentas do Git - Empacotando (Bundling)

Empacotando (Bundling)

Apesar de termos abordado as maneiras comuns de transferir dados do Git em uma rede (HTTP, SSH, etc), na verdade há uma maneira de mais de fazê-lo que não é muito usada mas que pode ser bastante útil.

O Git é capaz de “empacotar” (bundle) os seus dados em um único arquivo. Isso pode ser útil em vários cenários. Talvez a sua rede caiu e você quer enviar alterações para os seus colegas. Pode ser que você esteja trabalhando externo em algum lugar e não tem acesso à rede local por motivos de segurança. Talvez sua placa de rede wireless/ethernet simplesmente quebrou. Talvez você não tenha acesso a um servidor compartilhado no momento, quer mandar atualizações por email para alguém e não quer ter que transferir 40 commits por format-patch.

É aí que o comando git bundle pode ser útil. O comando bundle empacotará tudo que seria normalmente mandado (pushed) pela rede com um comando git push para dentro de um arquivo binário que você pode mandar por email para alguém ou pôr em um pen drive, para depois desempacotar (unbundle) em um outro repositório.

Vejamos um exemplo simples. Digamos que você tem um repositório com dois commits:

$ 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

Se você quiser enviar aquele repositório para alguém e você não tiver acesso a um repositório para o qual fazer o push, ou simplesmente não quiser configurar um, você pode empacotá-lo com 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)

Agora você tem um arquivo chamado repo.bundle que tem todos os dados necessários para recriar a branch master do repositório. Com o comando bundle você precisa listar todas as referências ou uma gama específica de commits que você quer que sejam incluídos. Se você pretende que isto seja clonado em algum lugar, você deveria adicionar o HEAD como referência, bem como nós fizemos aqui.

Você pode mandar este arquivo repo.bundle por email para alguém, ou pô-lo em um pendrive e levá-lo pessoalmente.

Na outra ponta, digamos que te enviem este arquivo repo.bundle e você queira trabalhar no projeto. Você pode clonar a partir do arquivo binário para um diretório, mais ou menos igual a quando você faz a partir de uma URL.

$ git clone repo.bundle repo
Cloning into 'repo'...
...
$ cd repo
$ git log --oneline
9a466c5 Second commit
b1ec324 First commit

Se você não incluir o HEAD nas referências, você tem também que especificar -b master ou seja qual for a branch incluída porque senão ele não saberá qual branch utilizar no check out.

Agora digamos que você faça três commits nisso e queira mandar os novos commits de volta através de um bundle em um pen drive ou por email.

$ git log --oneline
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo
9a466c5 Second commit
b1ec324 First commit

Primeiro nós precisamos determinar a gama de commits que queremos incluir no bundle. Diferente dos protocolos de rede, os quais calculam por nós qual é o conjunto mínimo de dados a se transferir pela rede, nós teremos de calcular isso manualmente. Você pode apenas fazer a mesma coisa e empacotar (bundle) o repositório inteiro, o que vai funcionar, mas é melhor empacotar apenas a diferença - somente os três commits que acabamos de fazer localmente.

A fim de fazer isso, você terá que calcular a diferença. Conforme descrevemos em Intervalos (Ranges) de Commits, você pode especificar uma gama de commits de uma série de formas. Para obtermos os três commits que temos na nossa branch master e que não estavam na branch que clonamos originalmente, nós podemos usar algo como origin/master..master ou master ^origin/master. Você pode testar isso com o comando log.

$ git log --oneline master ^origin/master
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo

Então, agora que temos a lista de commits que queremos incluir no bundle, vamos empacotá-los. Nós fazemos isso com o comando git bundle create, dando a ele um nome de arquivo que queremos que seja o nosso bundle e a gama de commits que queremos por ali dentro.

$ 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)

Agora nós temos um arquivo commits.bundle no nosso diretório. Se pegarmos isso e mandarmos para a nossa colega, ela então poderá importar para o repositório original, mesmo se mais trabalho tiver sido feito por lá nesse ínterim.

Quando ela receber o bundle, ela pode inspecioná-lo para ver o que ele contém antes que ela o importe para o repositório dela. O primeiro comando é o comando bundle verify que irá confirmar se o arquivo é realmente um bundle válido do Git e se você tem todos os ancestrais necessários para reconstituí-lo de forma correta.

$ 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

Se a pessoa tivesse criado um bundle apenas com os dois últimos commits que ela tinha feito, ao invés de todos os três, o repositório original não seria capaz de importá-lo, posto que estaria faltando histórico que lhe era requisito. O comando verify ter-se-ia parecido mais com isso, pelo contrário:

$ git bundle verify ../commits-bad.bundle
error: Repository lacks these prerequisite commits:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 Third commit - second repo

Entretanto, nosso primeiro bundle é válido, de modo que nós podemos fazer o fetch dos commits dali. Se você quer ver quais branches no bundle podem ser importadas, tem também um comando só pra listar os heads:

$ git bundle list-heads ../commits.bundle
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master

O subcomando verify também te contará os heads. O ponto é ver o que pode ser sofrer um pull (pulled in), de forma que você possa usar os comandos fetch ou pull para importar commits deste bundle. Aqui nós faremos o fetch da branch master do bundle, destinando a uma branch chamada other-master em nosso repositório:

$ git fetch ../commits.bundle master:other-master
From ../commits.bundle
 * [new branch]      master     -> other-master

Agora podemos ver que nós temos os commits importados na branch other-master assim como quaisquer commits que tenhamos feito nesse meio tempo em nossa própria branch 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

Sendo assim, o git bundle pode ser bastante útil para se compartilhar ou executar operações do tipo-rede quando você não dispõe da rede adequada ou de um repositório compartilhado para fazê-lo.