Aqui, eu irei examinar algumas partes internas do Git para ajudá-lo a entendê-lo de uma maneira mais fácil, eu pessoalmente acho que é uma ferramenta incrível e tenho certeza que muitas pessoas pensam o mesmo, Linus fez um ótimo trabalho.
Você pode seguir meu exemplo e criar um novo repositório Git apenas para usar como exemplo ou usar um que você já tenha.
Init e primeira versão
Vá para o seu terminal (OS X ou Linux, não vou explicar para Windows, se você quiser
fazer o download do Cygwin ) e digite isso para criar nosso repo:
~$ mkdir mynewrepo
~$ cd mynewrepo/
~/mynewrepo$ touch index.html
~/mynewrepo$ echo "<title>Coder Wall Post</title>" > index.html
~/mynewrepo$ git init
~/mynewrepo$ git add index.html
~/mynewrepo$ git commit -m "Created index page"
Você pode notar que uma pasta oculta chamada .git foi criada, é aqui que seu repositório fica e cada repo diferente tem uma pasta .git, fique à vontade para verificar o que temos dentro desta pasta.
Mudanças e segunda versão
Agora temos um repo com um arquivo e um commit , este commit é a primeira versão do nosso repo.
Vamos tentar isso:
~/mynewrepo$ echo "<div>The simplicity of Git</div>" >> index.html
~/mynewrepo$ cat index.html
<title>Coder Wall Post</title>
<div>The simplicity of Git</div>
~/mynewrepo$ git add index.html
~/mynewrepo$ git commit -m "Changes made on index.html"
Acabamos de criar a segunda versão dele. Agora vem a parte legal, vamos cavar no .git.
Mergulhando no Git
Vá para a pasta mynewrepo / .git , você verá que há uma pasta chamada objetos / , esta pasta é onde está o “banco de dados de objetos” do Git, cada arquivo nesta subpasta é um objeto e pode ser um blob (representa um arquivo), uma árvore (vários blob ou objetos de árvore), um commit (versão do nosso código) ou uma tag .
~/mynewrepo/.git/objects$ find .
./1e/172dd78d8d0c06d70dc2368d2473d8823dfef1
./52/fd4a0493ef07bec1e4bbef605f53417cf268b9
./75/ea055e99cf2facf24b33125c0afff33cc12235
./a0/47435aad7147188c24614c01d46442ecae2b55
./d9/26b468aa2bd7e5d530ff4328a9d74f01c07c66
./e8/64d2df7cc14394b814459908b894e61ca93dba
./info
./pack
Dentro de um objeto Git
Esses objetos são todos compactados com zlib, para ler o conteúdo dos objetos, podemos usar a ferramenta fornecida pelo git.
Lendo o objeto com :git cat-file
~/mynewrepo/.git/objects$ git cat-file -p d926b468aa2bd7e5d530ff4328a9d74f01c07c66
<title>Coder Wall Post</title>
A saída é o conteúdo de index.html , portanto sabemos que é um objeto blob .
Vamos tentar outro:
~/mynewrepo/.git/objects$ git cat-file -p 75ea055e99cf2facf24b33125c0afff33cc12235
tree 52fd4a0493ef07bec1e4bbef605f53417cf268b9
parent e864d2df7cc14394b814459908b894e61ca93dba
author Lucas Rinaldi <...@gmail.com> 1378043411 +0200
committer Lucas Rinaldi <...@gmail.com> 1378043411 +0200
Changes made on index.html
Este é um objeto de commit , é nosso último commit, verifique o comentário que adicionamos anteriormente.
Cada um desses nomes de arquivo de objetos são Hashes SHA1 de seu conteúdo com alguns dados do usuário juntos, então se você estiver acompanhando, observe que seus nomes de arquivo não são iguais a minas. Git chama essa coisa de nomenclatura de “conteúdo endereçável”.
Então, o que diabos isso significa?
O objeto de confirmação é a versão completa de seu código no momento de sua confirmação. Tudo que você precisa saber está incluído nele:
tree 52fd4a0493ef07bec1e4bbef605f53417cf268b9
parent e864d2df7cc14394b814459908b894e61ca93dba
author Lucas Rinaldi <...@gmail.com> 1378043411 +0200
committer Lucas Rinaldi <...@gmail.com> 1378043411 +0200
O hash que aparece depois tree
, indica o estado da árvore do repositório no momento, quais arquivos e pastas estão dentro dela, vamos verificar:
~/mynewrepo/.git/objects$ git cat-file -p 52fd4a0493ef07bec1e4bbef605f53417cf268b9
100644 blob 1e172dd78d8d0c06d70dc2368d2473d8823dfef1 index.html
Como temos apenas um arquivo, é apenas o que aparecerá na árvore. Não vou adicionar mais arquivos e pastas porque este texto já é grande o suficiente, mas tente adicionar, especialmente pastas a ele, você verá como é legal a maneira como eles lidam com isso … cof cof..recur..cof .. sion.
E o parent
hash é apenas o ponteiro para o commit anterior feito.
Ramos, cabeças, refs, tags
Para este pequeno projeto é fácil encontrar o último commit feito, e verificar todos os arquivos, mas você pode imaginar para um grande projeto? Quanto tempo levaria para encontrar todas as informações?
É por isso que git anote o hash do último commit feito:
~/mynewrepo/.git$ cat refs/heads/master
75ea055e99cf2facf24b33125c0afff33cc12235
E agora você acabou de ver o que é um branch , apenas o hash do último commit feito no branch, e isso leva a todo o histórico dele.
~/mynewrepo/.git$ git checkout -b new_branch
Switched to a new branch 'new_branch'
~/mynewrepo/.git$ ls refs/heads/
$ ls refs/heads/
master
new_branch
~/mynewrepo/.git$ cat HEAD
ref: refs/heads/new_branch
~/mynewrepo/.git$ cat refs/heads/new_branch
75ea055e99cf2facf24b33125c0afff33cc12235
Como o git oferece suporte a vários branches, eles escrevem em qual branch você está trabalhando:
~/mynewrepo/.git$ cat HEAD
ref: refs/heads/master
Ele pode conter o hash do commit ou o nome do branch. E é a mesma coisa com as tags, apenas um hash do commit marcado.
Conclusão
Espero que este pequeno resumo do git guts tenha ajudado você a entender e ficar mais interessado nele, para mim o git não é muito direto, acho que seus comandos não são semânticos e às vezes me confundo com eles. Saber o que escrevi aqui me ajudou a entender melhor a máquina.
Sinta-se à vontade para adicionar mais conhecimento :).