Pontos de dados

Git: São apenas dados!

Julie Lerman

Baixar o código de exemplo

Julie LermanControle de fonte em uma coluna de dados? Ah, mas quando aquele controle de fonte é apenas um grande banco de dados, é um convite para diversão dos especialistas em dados. Para ser claro, você deve saber logo que não estou escrevendo sobre o Git este mês porque sou uma especialista. Na verdade, estou escrevendo sobre o Git porque tive dificuldade com ele. O meu perfil GitHub um pouco anêmico é uma prova disso. Quando o assunto do Git surge, eu tendo a mudar de assunto por medo que a minha falta de conhecimento do Git seja descoberta—já que a maioria dos meus amigos desenvolvedores interagem com ele sem hesitar.

Ao falar da minha dificuldade com o Git recentemente ao meu amigo e desenvolvedor local do Ruby, Alan Peabody (github.com/alanpeabody), em um happy hour de hacker local, ele me disse, “Mas Julie, são apenas dados”. Dados? “Oh, eu amo dados! Me fale mais!” Peabody descreveu como o Git depende de um banco de dados arquivado com pares de chave/valor, e depois sugeriu que, em vez de tentar usar as ferramentas da interface de usuário disponíveis ou o que é conhecido como comandos “porcelana” para trabalhar com o Git, eu explore e brinque com os comandos de “encanamento” e APIs.

Então o Git tem um banco de dados e um encanamento acessível! O conselho de Peabody certamente me inspirou. Tenho agora dedicado algum tempo explorando como o Git funciona no nível inferior, como ler e gravar dados que representam a atividade de repositório, e como os diferentes tipos de objetos no Git se relacionam uns com os outros. Eu continuo longe de ser uma especialista, mas me sinto muito mais em controle de minhas próprias atividades sobre o Git. Mais importante, estou me divertindo com o Git e não tenho medo dele como uma fonte de magia negra.

Se esse caminho atrai você, recomendo que não perca os capítulos “Git Basics” e “Git Internals” do livro online “Pro Git” de Scott Chacon (Apress, 2009) e disponível em git-scm.com/book.

Tenho certeza que o banco de dados Git foi indicado para mim antes, mas somente de passagem e enquanto eu já estava impressionada com a curva de aprendizagem. Agora eu planejo me divertir um pouco com isso. Para o restante deste artigo, eu simplesmente interajo com o banco de dados e com o mesmo código que está rastreando e ver como ele afeta o banco de dados que representa o repositório.

Em vez de iniciar com um repositório novo vazio, eu usarei um repositório existente com o qual já estava brincando: o trabalho bem inicial sobre o Entity Framework 7 hospedado em GitHub.com/aspnet/Entity­Framework. Lembre-se de que esse é um projeto que está mudando rapidamente no momento em que estou escrevendo esta coluna, portanto, os arquivos que eu trabalho aqui podem mudar.

Eu executarei os comandos do Windows PowerShell, aproveitando o elegante git, que aprimora a experiência da linha de comando com informações de status adicionais, codificação por cores e conclusão de tabulação. Você pode encontrar instruções para configurar isso no download que acompanha este artigo.

Obter um repositório e examinar os ativos do git

A primeira etapa é clonar o repositório existente, começando em minha pasta github existente e usando o comando de clone do git:

D:\User Documents\github> git clone git://github.com/aspnet/EntityFramework

Por si só, esta primeira etapa elevou minhas habilidades no Git! O Git cria uma nova pasta no meu diretório inicial usando o nome do repositório, então eu obtenho D:\User Documents\github\EntityFramework. Durante o processo de clonagem, se você abrir a pasta EntityFramework no Explorador de Arquivos logo que ela for criada, verá que uma subpasta .git é criada antes. Esse é o repositório e é o que chamamos de banco de dados git. E o Git está usando esses dados para construir a fonte que está sendo referida como o diretório de trabalho, ou seja, o restante da pasta do EntityFramework.

Quando a operação é concluída, a nova pasta, EntityFramework, se parece com qualquer outra pasta de solução do Visual Studio exceto por uma coisa: a pasta .git que é, por si, uma cópia completa do repositório clonado, incluindo todas as ramificações. Observe a estrutura do arquivo na Figura 1. No Git, uma ramificação é apenas um indicador para uma confirmação e uma confirmação é um indicador para um instantâneo do seu diretório de trabalho. Embora a ramificação “principal” do repositório Git seja chamada de mestre por padrão, não é uma exigência. A equipe do EF definiu sua ramificação padrão para apontar para uma ramificação que chamaram de dev. Ao usar as ramificações, você pode modificar e testar códigos com segurança antes de mesclar alterações em outras ramificações.

A pasta solução criada através da clonagem do repositório, incluindo o próprio repositório na pasta .git
Figura 1 A pasta solução criada através da clonagem do repositório, incluindo o próprio repositório na pasta .git

O restante do conteúdo da pasta EntityFramework representa o diretório de trabalho. Neste mesmo contexto, você diz para o Git controlar os arquivos que você adicionou, alterou ou excluiu no seu diretório de trabalho. Isso é referido como “preparado”—ou seja, as alterações para estes arquivos estão preparadas e prontas para serem confirmadas. As alterações preparadas são armazenadas no banco de dados e permanecerão lá após você ter confirmado elas. Eventualmente, você envia as alterações para o servidor. Não tratarei dessa etapa neste artigo porque meu foco está na exploração da atividade do banco de dados. Existem muitos outros conceitos, como árvores e bifurcações, referências e cabeçalhos que também irei ignorar para esta jornada.

Então o que é e onde está o banco de dados? É um banco de dados relacional como o SQL Server ou o SQL CE? Não. É uma coleção de arquivos que representa objetos Git. Cada arquivo é nomeado com um hash e contém conteúdo com hash. Lembre-se de que o banco de dados é um conjunto de pares de chave/valor. O nome do arquivo é a chave que representa aquele objeto no banco de dados e seus conteúdos são o valor. A coleção desses objetos inclui o banco de dados que representa o repositório. Todos os pares de chave/valor são armazenados na pasta .git/objects e uma lista mestre deles é armazenada em um arquivo chamado index. Há outros objetos que controlam as diferentes ramificações. É possível que muitos arquivos sejam apenas duplicados e nunca editados. O Git tem uma maneira de apontar para esses arquivos ao invés de manter cópias separadas, que impede o banco de dados Git de inchar.

Mas não há arquivos de objeto a serem vistos ainda, pois inicialmente, tudo é compactado em um arquivo do pacote juntamente com seu equivalente IDX. Eles estão na subpasta PACK conforme exibido na Figura 2.

Você não verá objetos no início—todos eles estão compactados em um arquivo PACK
Figura 2 Você não verá objetos no início—todos eles estão compactados em um arquivo PACK

O que o Git sabe sobre meu código?

Antes de começar a editar, quero verificar o que o Git sabe sobre meu diretório de trabalho e sobre o repositório. Isso significa voltar ao Windows PowerShell e habilitar o posh-git para aprimorar a minha experiência de linha de comando do Git. Eu começo alterando o diretório para a pasta EntityFramework (cd EntityFramework, simplesmente como nos bons velhos tempos do DOS). O posh-git verá a subpasta .git e se envolverá no ambiente do Windows PowerShell. A Figura 3 mostra que a minha janela do Windows PowerShell agora tem um título posh~git e o prompt exibe um status em parênteses amarelo—esses são recursos do posh-git. Atualmente, isso indica que o meu diretório de trabalho está usando a ramificação chamada dev, a ramificação principal do repositório do EF7. Quando eu clonei o repositório, por padrão o Git fez o “check out” desta ramificação e essa é a versão que ele colocou no meu diretório de trabalho. Isso é realmente tudo que o check out significa para o Git. Ele cria o diretório de trabalho pela ramificação designada. Um exercício divertido é excluir tudo, exceto a pasta .git no Explorador de Arquivos, e digite “git checkout dev” no Windows PowerShell para ver o conteúdo do diretório ser completamente recriado. Desconecte-se da Internet se deseja comprovar que não está vindo do servidor.

Windows PowerShell com posh-git ativado
Figura 3 Windows PowerShell com posh-git ativado

Eu posso perguntar para o Git o que ele sabe sobre meu diretório de trabalho com o comando “status” do git. Todos os comandos git começam abordando o git, portanto, digite:

git status

Ele responde com a mensagem:

On branch dev
nothing to commit, working directory clean

Isso faz sentido. Estou começando com uma ficha limpa.

Veja como o git responde para editar seus arquivos do diretório de trabalho

Agora é hora de começar a bater nas coisas para ver este status mudar.

Vou editar uma das minhas classes EF favoritas: DbContext.cs (que está em src\EntityFramework). Nunca vou me preocupar com o Visual Studio. Você pode apenas usar o NotePad++ ou o seu editor de texto favorito.

Eu adicionei um comentário na parte superior:

// Julie was here

e salvei.

Quando executei o status do git novamente, a resposta é mais interessante agora, como mostra a Figura 4.

Status após modificar um arquivo no diretório de trabalho (a fonte vermelha indica o estado do diretório de trabalho)
Figura 4 Status após modificar um arquivo no diretório de trabalho (a fonte vermelha indica o estado do diretório de trabalho)

O Git vê que o arquivo (observado em uma fonte vermelha difícil de ler, infelizmente) foi alterado, mas não está selecionado. Em outras palavras, o Git ainda não está monitorando ele, mas tem comparado os arquivos do diretório de trabalho com seu banco de dados para tomar esta decisão. Observe também que o novo status do prompt para dev é +0 ~1 -0. O status exibido em vermelho reflete o diretório de trabalho, aqui indicando 0 novos arquivo, 1 arquivo modificado e nenhum arquivo excluído.

E quanto ao banco de dados? Se olhar no Explorador de Arquivos, você verá pelo carimbo de data e tamanho que o arquivo de índice não foi alterado. Nada foi alterado na pasta objetos, também. O banco de dados Git não sabe da alteração no meu diretório de trabalho.

Caro Git, monitore meu arquivo agora

O Git não monitorará os arquivos até você pedir para ele monitorar. Com os comandos porcelana típicos, você simplesmente diz para o Git adicionar e ele descobre qual arquivo atrair. No entanto, vou usar um comando de encanamento em vez dos comandos mais comuns que os desenvolvedores usam com o Git assim posso ser bastante explícito sobre cada passo que quero dar. Observe que o caminho do arquivo que digito diferencia maiúsculas de minúsculas:

git update-index --add src\EntityFramework\DbContext.cs

Em resposta, o status do prompt altera. Continua dizendo +0 ~1 -0, mas a fonte está verde agora, não vermelha, indicando que esse é o status do índice. O Git agora está monitorando 0 novos objetos, 1 objeto modificado e 0 objetos excluídos, que estão pontos para fazerem a confirmação.

E quando ao arquivo de índice do Git e a pasta objetos?

O carimbo de data/hora do arquivo de índice no diretório .git foi alterado. O arquivo agora tem uma notação que o arquivo de objeto que representa a classe DbContext.cs foi alterado. É assim que o status sabe que um arquivo modificado está sendo monitorado. Para os codificadores do Entity Framework, isto parece familiar? O índice é semelhante ao EF DbContext, que controla alterações nas suas entidades! O DbContext sabe quando uma instância de objeto foi adicionada, modificada ou excluída. O arquivo de índice é semelhante desta maneira.

Mas agora você pode ver o objeto também. Navegue para .git/objects e você verá uma nova pasta. Se você editou no Visual Studio, é possível ver mais, por exemplo, se o arquivo de projeto foi alterado. Mas eu editei no Bloco de Notas++ e há apenas uma nova pasta, chamada ae. Na pasta tem um arquivo com um nome que é um hash, como exibido na Figura 5.

Um objeto sendo monitorado pelo Git na sua pasta objetos
Figura 5 Um objeto sendo monitorado pelo Git na sua pasta objetos

Esse objeto faz parte do meu banco de dados e substitui o objeto que representa o arquivo DbContext.cs que está em cache no arquivo PACK. Aquele novo arquivo de objeto contém um hash do conteúdo do DbContext.cs, incluindo minha alteração.

Não consigo ler sozinho, mas o Git pode. Pedirei ao Git que exiba seu conteúdo todo com o comando cat-file:

git cat-file ­-p ­aeb6db24b9de85b7b7cb833379387f1754caa146

O parâmetro -p solicita uma lista "embelezada" do texto. É seguido pelo nome do objeto a ser listado, que é uma combinação do nome da pasta (ae) e o nome do arquivo. O Git tem atalhos para expressar esse nome de objeto.

Uma exibição mais interessante é uma que destaca a alteração. Posso solicitar ao Git para me mostrar o que foi alterado nessa ramificação (lembre-se que seu nome é “dev”) com:

git diff dev

A resposta, para a qual adicionei números de linha para clareza, é exibida na Figura 6.

Resposta para o comando git diff
Figura 6 Resposta para o comando git diff

Este texto foi formatado usando fontes coloridas que facilitam distinguir as diferentes informações. Observe que na linha 8, há um traço (que é vermelho), indicando que exclui uma linha [em branco]. A linha 9 começa com um sinal de adição, indicando uma nova linha, e é exibido em fonte verde. Se eu adicionei mais objetos ao índice—para arquivos modificados e novos—eles estariam listados aqui também.

Mesmo que o arquivo de índice seja um binário, também posso explorá-lo. O comando git ls-files é útil para listar todos os arquivos representados por objetos de repositório no arquivo de índice. Infelizmente, o ls-files lista uma combinação do índice em cache e do diretório de trabalho, mostrando todos os arquivos em minha solução, portanto, eu tenho que procurar para encontrar os arquivos que estou interessado. Existe outro comando git, o grep, que é usado para filtrar. Você pode combinar isso com o ls-files para filtrar os arquivos. Adicionarei o grep (que faz distinção de maiúsculas e minúsculas) e também solicitarei ao ls-files para exibir o nome do objeto usando o parâmetro (selecionado) it -s:

git ls-files -s |grep DbContext.cs

Incluir o parâmetro selecionado força o resultado a conter um indicador de modo de arquivo (100664 significa que é um arquivo gravável do grupo não executável) e o nome do arquivo hash do objeto com o qual ele está associado:

100644 aeb6db24b9de85b7b7cb833379387f1754caa146 0
src/EntityFramework/DbContext.cs

Há muito mais em relação ao Is-files, mas é nisto que estou interessada no momento—o fato de que eu posso ver como o Git está mapeando o arquivo de objeto para o arquivo DbContext.cs do diretório de trabalho.

O que a confirmação faz para o banco de dados Git?

Em seguida, vamos ver um pouco dos efeitos de enviar objetos do estado selecionado para o confirmado. Lembre-se que essa confirmação somente ocorre no seu computador. Nada é confirmado diretamente ao repositório original no servidor. O comando git commit necessita que você adicione uma nota usando o parâmetro -m. Tenha em mente que você está confirmando todas as alterações selecionadas. Eu fiz apenas uma aqui:

git commit -m "Edited DbContext.cs"

O Git responde dizendo para mim o nome do banco de dados Git recentemente gerado do objeto (2291c49) que contém a informação de confirmação e o que foi confirmado. A nota de inserção é sobre o que aconteceu no arquivo—eu adicionei algo:

[dev 2291c49] Edited DbContext.cs
  1 file changed, 1 insertion(+)
  D:\User Documents\github\ 
    entityframework [dev]>

Observe que o prompt está limpo. O status agora reflete tudo que aconteceu desde a última confirmação, que não foi nada. Estou de volta para uma ficha limpa.

O que aconteceu no meu banco de dados em resposta à confirmação? Eu posso dizer que o arquivo de índice não estava atualizado porque seu carimbo de data/hora não foi alterado.

Porém há quatro novas pastas no diretórios de objetos, cada uma contendo seu próprio objeto hash. Eu usei o cat-file para ver o que tem nestes arquivos. O primeiro é uma lista, semelhante ao ls-files, de todos os arquivos e pastas na pasta src\EntityFramework, juntamente com seus nomes de objeto. O segundo é uma lista de todos os objetos na pasta src, ou seja, uma lista das subpastas. Essas, a propósito, são “árvores”, não “pastas”, na linguagem do git. O terceiro objeto contém uma lista de todos os arquivos e pastas na pasta raiz EntityFramework. O objeto final contém dados que serão necessários para sincronizar para o servidor—minha identidade do autor do commit (meu endereço de email), e um objeto para “árvore” e outro para “pai”. Curiosamente, esse objeto final é o mesmo objeto que foi retransmitido em resposta à confirmação. Está em uma pasta chamada 22 e seu nome de arquivo começa com 91c49.

Todos estes objetos serão usados para incrementar o repositório do servidor quando chegar a hora.

Me apropriei do Git?

De certa forma, eu me apropriei do Git ao explorar as coisas em um nível inferior. Eu adoro explorar causa e efeito, bem como explorar o encanamento. Eu definitivamente perdi o medo da mágica do Git e brincar com ele desta maneira tem também me dado favoravelmente um entendimento da linguagem de comando e da beleza de trabalhar com o Git em um nível inferior. E é provavelmente o maior tempo que já me dediquei ao Windows PowerShell, também.

Tenho amigos que são fanáticos pelo Git—como Cori Drew que entrou no Skype comigo várias vezes quando fiquei confusa com o que eu estava vendo—e vários outros que usam o Git como se fosse o próprio ar que respirassem. Eles tentaram me mostrar como o Git funciona, mas inicialmente eu desisti dele. Esse exercício me capacitou para saber mais e me beneficiar dele e para ver quanto mais tem para aprender. Mas é apenas o meu fascínio com dados e com bater nas coisas para ver como elas reagem que me trouxeram a este ponto. E por isso sou grata pela sugestão inspiradora de Alan Peabody.

Atualização: Um semana depois de originalmente terminar de escrever este artigo, me encontrei usando o Git em dois projetos solos, fazendo ramificações e mesclagem quase como um profissional! Meu único mergulho foi um grande sucesso e espero que o seu seja também.                                                        


Julie Lerman é MVP da Microsoft, mentora e consultora do .NET, que reside nas colinas de Vermont. Você pode encontrá-la em apresentações sobre acesso de dados ou sobre outros tópicos .NET em grupos de usuários e conferências em todo o mundo. Ela escreve no blog em thedatafarm.com/blog e é a autora de “Programming Entity Framework” (2010), bem como uma edição do Code First (2011) e uma edição do DbContext (2012), todos da O’Reilly Media. Siga Julie no Twitter em twitter.com/julielerman e confira seus cursos da Pluralsight em juliel.me/PS-Videos.

Agradecemos aos seguintes especialistas técnicos pela revisão deste artigo: Cori Drew (coridrew@gmail.com) e Alan Peabody (gapeabody@gmail.com)