Protocolos de Transferência
Git pode transferir dados entre dois repositórios de duas maneiras principais: através de HTTP e através dos protocolos chamados inteligentes: file://
, ssh://
e git://
. Esta seção irá mostrar rapidamente como esses dois principais protocolos operam.
O Protocolo Burro
HTTP é muitas vezes referido como o protocolo burro, porque não requer código Git específico do lado servidor durante o processo de transporte. O processo de fetch é uma série de solicitações GET, onde o cliente pode assumir o layout do repositório Git no servidor. Vamos ver o processo http-fetch
para obter a biblioteca simplegit:
$ git clone http://github.com/schacon/simplegit-progit.git
A primeira coisa que este comando faz é obter o arquivo info/refs
. Este arquivo é escrito pelo comando update-server-info
, é por isso que você precisa ativar ele como um hook post-receive
para que o transporte HTTP funcione corretamente:
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
Agora você tem uma lista de referências remotas e os SHAs. Em seguida, você verifica qual é a referência HEAD para que você saiba o que deve ser obtido (check out) quando você terminar:
=> GET HEAD
ref: refs/heads/master
Você precisa fazer o check out do branch master
quando você tiver concluído o processo.
Neste momento, você está pronto para iniciar o processo de "caminhada". Como o seu ponto de partida é o objeto commit ca82a6
que você viu no arquivo info/refs
, você começa obtendo ele:
=> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
Você obtêm um objeto — este objeto é um "loose format" no servidor, e você o obteu a partir de uma conexão HTTP GET estática. Você pode descompactá-lo, retirar o cabeçalho, e ver o conteúdo do commit:
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <[email protected]> 1205815931 -0700
committer Scott Chacon <[email protected]> 1240030591 -0700
changed the version number
A seguir, você tem mais dois objetos para obter — cfda3b
, que a árvore de conteúdo do commit que acabamos de obter referencia, e 085bb3
, que é o commit pai:
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
Isso lhe dá o seu próximo objeto commit. Obtenha o objeto árvore:
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
Oops — parece que esse objeto árvore não está em "loose format" no servidor, então você recebe um erro 404. Há algumas razões para isso — o objeto pode estar em um repositório alternativo, ou poderia estar em um packfile neste repositório. Git verifica a existência de quaisquer alternativas listadas primeiro:
=> GET objects/info/http-alternates
(empty file)
Se isso retornar uma lista de URLs alternativas, Git verifica a existência de "loose files" e packfiles lá — este é um mecanismo legal para projetos que são forks de um outro para compartilhar objetos no disco. No entanto, como não há substitutos listados neste caso, o objeto deve estar em um packfile. Para saber quais packfiles estão disponíveis neste servidor, você precisa obter o arquivo objects/info/packs
, que contém uma lista deles (também gerado pelo update-server-info
):
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
Há apenas um packfile no servidor, então o seu objeto deve estar lá, mas você vai verificar o arquivo de índice para ter certeza. Isso também é útil se você tem vários packfiles no servidor, assim você pode ver qual packfile contém o objeto que você precisa:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
Agora que você tem o índice do packfile, você pode ver se o seu objeto esta nele — porque o índice lista os SHAs dos objetos contidos no packfile e os deslocamentos (offsets) desses objetos. Seu objeto está lá, então vá em frente e obtenha todo o packfile:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
Você tem o seu objeto árvore, então você continua navegando em seus commits. Eles todos estão dentro do packfile que você acabou de baixar, então você não precisa fazer mais solicitações para o servidor. Git faz o check out de uma cópia de trabalho do branch master
, que foi apontada pela referência HEAD que você baixou no início.
Toda a saída deste processo é parecida com isto:
$ git clone http://github.com/schacon/simplegit-progit.git
Initialized empty Git repository in /private/tmp/simplegit-progit/.git/
got ca82a6dff817ec66f44342007202690a93763949
walk ca82a6dff817ec66f44342007202690a93763949
got 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Getting alternates list for http://github.com/schacon/simplegit-progit.git
Getting pack list for http://github.com/schacon/simplegit-progit.git
Getting index for pack 816a9b2334da9953e530f27bcac22082a9f5b835
Getting pack 816a9b2334da9953e530f27bcac22082a9f5b835
which contains cfda3bf379e4f8dba8717dee55aab78aef7f4daf
walk 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
walk a11bef06a3f659402fe7563abf99ad00de2209e6
O Protocolo Inteligente
O método HTTP é simples, mas um pouco ineficiente. Usar protocolos inteligentes é um método mais comum de transferência de dados. Estes protocolos têm um processo no sistema remoto que é inteligente em relação ao Git — ele pode ler dados locais e descobrir o que o cliente tem ou precisa e gera dados personalizados para ele. Existem dois conjuntos de processos de transferência de dados: um par para enviar dados (upload) e um par para download de dados.
Fazendo Upload de Dados
Para fazer upload de dados para um processo remoto, Git usa os processos send-pack
e receive-pack
. O processo send-pack
é executado no cliente e se conecta a um processo receive-pack
no lado remoto.
Por exemplo, digamos que você execute git push origin master
em seu projeto, e origin
é definido como uma URL que usa o protocolo SSH. Git executa o processo send-pack
, que inicia uma conexão SSH ao seu servidor. Ele tenta executar um comando no servidor remoto através de uma chamada SSH que é parecida com isto:
$ ssh -x [email protected] "git-receive-pack 'schacon/simplegit-progit.git'"
005bca82a6dff817ec66f4437202690a93763949 refs/heads/master report-status delete-refs
003e085bb3bcb608e1e84b2432f8ecbe6306e7e7 refs/heads/topic
0000
O comando git-receive-pack
imediatamente responde com uma linha para cada referência que tem atualmente — neste caso, apenas o branch master
e seu SHA. A primeira linha também tem uma lista de recursos do servidor (aqui, report-status
e delete-refs
).
Cada linha se inicia com um valor hexadecimal de 4 bytes especificando quão longo o resto da linha é. Sua primeira linha começa com 005B, que é 91 em hexadecimal, o que significa que 91 bytes permanecem nessa linha. A próxima linha começa com 003e, que é 62, então você leu os 62 bytes restantes. A próxima linha é 0000, ou seja, o servidor terminou com a sua lista de referências.
Agora que ele sabe o estado do servidor, o seu processo send-pack
determina que commits ele tem mas que o servidor não tem. Para cada referência que este push irá atualizar, o processo send-pack
diz essa informação ao processo receive-pack
. Por exemplo, se você está atualizando o branch master
e está adicionando um branch experiment
, a resposta do send-pack
pode ser parecida com esta:
0085ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 refs/heads/master report-status
00670000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d refs/heads/experiment
0000
O valor SHA-1 de todos os '0's significa que nada estava lá antes — porque você está adicionando a referência de experiment. Se você estivesse apagando uma referência, você veria o oposto: tudo '0' no lado direito.
Git envia uma linha para cada referência que você está atualizando com o SHA antigo, o SHA novo, e a referência que está sendo atualizada. A primeira linha também tem as capacidades do cliente. Em seguida, o cliente faz upload de um packfile com todos os objetos que o servidor não tem ainda. Finalmente, o servidor responde com uma indicação de sucesso (ou falha):
000Aunpack ok
Fazendo Download de Dados
Quando você baixa os dados, os processos fetch-pack
e upload-pack
são usados. O cliente inicia um processo fetch-pack
que se conecta a um processo upload-pack
no lado remoto para negociar os dados que serão transferidos para a máquina local.
Existem diferentes maneiras de iniciar o processo upload-pack
no repositório remoto. Você pode fazê-lo via SSH da mesma forma que o processo receive-pack
. Você também pode iniciar o processo, através do daemon Git, que escuta em um servidor na porta 9418 por padrão. O processo fetch-pack
envia dados que se parecem com isso para o servidor após se conectar:
003fgit-upload-pack schacon/simplegit-progit.git\0host=myserver.com\0
Ele começa com os 4 bytes que especificam a quantidade de dados enviados, seguido pelo comando a ser executado, seguido por um byte nulo e, em seguida, o hostname do servidor seguido por um ultimo byte nulo. O daemon Git verifica que o comando pode ser executado e que o repositório existe e tem permissões públicas. Se tudo estiver certo, ele aciona o processo upload-pack
e envia o pedido para ele.
Se você estiver fazendo o fetch através de SSH, fetch-pack
executa algo como isto:
$ ssh -x [email protected] "git-upload-pack 'schacon/simplegit-progit.git'"
Em todo caso, depois que fetch-pack
conectar, upload-pack
devolve algo como isto:
0088ca82a6dff817ec66f44342007202690a93763949 HEAD\0multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
003e085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 refs/heads/topic
0000
Isto é muito semelhante a resposta de receive-pack
, mas as funcionalidades são diferentes. Além disso, ele envia de volta a referência de HEAD para que o cliente saiba o que deve ser verificado (check out) se este for um clone.
Neste momento, o processo fetch-pack
verifica quais objetos ele possui e responde com os objetos de que necessita através do envio de "want" e do SHA que quer. Ele envia todos os objetos que ele já tem com "have" e também o SHA. No final da lista, ele escreve "done" para iniciar o processo upload-pack
para começar a enviar o packfile dos dados que ele precisa:
0054want ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0000
0009done
Isto é um caso muito simples dos protocolos de transferência. Em casos mais complexos, o cliente suporta funcionalidades multi_ack
ou side-band
; mas este exemplo mostra o funcionamento de upload e download básico utilizado pelos processos de protocolo inteligentes.