Você pode executar algo como git log 1a410e
para ver todo o seu histórico, mas você ainda precisa lembrar que 1a410e
é o último commit para poder caminhar nesse histórico para encontrar todos esses objetos.
Você precisa de um arquivo em que você possa armazenar o valor do SHA-1 com um simples nome para que você possa usar essa referência em vez do valor de um SHA-1 puro.
No Git, chamamos isso de referências'' (references) ou
refs''; você pode encontrar os arquivos que contém os valores SHA-1 no diretório .git/refs
.
No projeto atual, este diretório não contém nenhum arquivo, mas contém uma simples estrutura:
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
Para criar uma nova referência que irá te ajudar a lembrar onde está seu último commit, você pode tecnicamente fazer algo tão simples quanto isto:
$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master
Agora, você pode usar a referência head que você acabou de criar em vez do valor SHA-1 nos seus comandos Git:
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
Nós não encorajamos você a editar diretamente arquivos de referência.
O Git provê um comando mais seguro para fazer isso se você quiser atualizar uma referência, chamado update-ref
:
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
Isto é o que uma branch é basicamente: uma simples referência para a cabeça de uma linha de trabalho. Para criar uma branch no segundo commit, você pode fazer isto:
$ git update-ref refs/heads/test cac0ca
Sua branch irá conter apenas o trabalho a partir desse commit:
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
Agora, seu banco de dados do Git conceitualmente aparenta ser algo assim:
Quando você executa comandos como git branch (nome da branch)
, o Git basicamente executa esse comando update-ref
para adicionar o SHA-1 do último commit da branch que você está em qualquer nova referência que você quer criar.
A questão agora é, quando você executa git branch (nome da branch)
, como o Git sabe o SHA-1 do último commit?
A resposta é o arquivo HEAD.
O arquivo HEAD é uma referência simbólica para a branch que você está no momento. Queremos dizer por referência simbólica que, ao contrário de uma referência normal, em geral ela não contém um valor de um SHA-1, mas um ponteiro para outra referência. Se você olhar para o arquivo, normalmente você verá algo como isto:
$ cat .git/HEAD
ref: refs/heads/master
Se você executar git checkout test
, o Git atualizará o arquivo de forma que ele ficará assim:
$ cat .git/HEAD
ref: refs/heads/test
Quando você executa git commit
, ele cria um objeto commit, especificando como pai desse objeto commit o valor do SHA-1 que a referência contida em HEAD aponta.
Você pode alterar manualmente esse arquivo mas, novamente, um comando mais seguro existe para fazer isso: symbolic-ref
.
Você pode ler esse arquivo de sua HEAD através deste comando:
$ git symbolic-ref HEAD
refs/heads/master
Você também pode definir o valor de HEAD:
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
Você não pode definir o valor de uma referência simbólica fora do estilo refs:
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
Nós acabamos de falar sobre os três principais tipos de objetos, mas existe também um quarto. O objeto tag é bem parecido com um objeto commit, ele contém um tagger, a data, a mensagem, e uma referência. A principal diferença é que um objeto tag geralmente aponta para um commit em vez de uma tree. Ele é bem parecido com uma referência do tipo branch, mas ele nunca se move - ele sempre aponta para o mesmo commit mas dá a ele um nome mais amigável.
Como discutimos em ch02-git-basics.asc, exitem dois tipos de tags: anotada e leve. Você pode criar uma tag leve executando algo assim:
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
Isso é tudo que uma tag leve é - uma referência que nunca se move.
Entretanto, uma tag anotada é mais complexa.
Se você criar uma tag anotada, o Git cria um objeto tag e então escreve uma referência que aponta para ele em vez de apontar diretamente para o commit.
Você pode ver isso criando uma tag anotada (-a
especifica que é uma tag anotada):
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
Aqui está o valor SHA-1 do objeto que foi criado:
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
Agora, execute o comando cat-file
no valor SHA-1:
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700
test tag
Note que o item object
aponta ao SHA-1 do commit que você criou a tag.
Note também que ele não precisa apontar para um commit; você pode adicionar qualquer objeto do Git.
No código-fonte do Git, por exemplo, o mantenedor adicionou sua chave GPG pública como um objeto blob e então criou uma tag para ele.
Você pode ver a chave pública executando isto em um clone do repositório do Git:
$ git cat-file blob junio-gpg-pub
O repositório do kernel Linux também tem um objeto tag que não aponta para um commit: a primeira tag criada aponta para a tree inicial da importação do código-fonte.
O terceiro tipo de referência que você verá é o remote (remoto).
Se você adicionar um remote e fizer um push para ele, o Git armazenará o valor que você fez o push para ele para cada branch no diretório refs/remotes
.
Por exemplo, você pode adicionar um remote chamado origin
e fazer o push da sua master
para ele:
$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
Então, você pode ver onde a branch master
no remote origin
estava na última vez que você se comunicou com o servidor, olhando o arquivo refs/remotes/origin/master
:
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
Remotes se diferenciam de branches (referências em refs/heads
) principalmente pelo fato de normalmente serem somente-leitura.
Você pode executar git checkout
para um remote, mas o Git não irá apotar a HEAD para um, então você nunca irá atualizá-la com um commando commit
.
O Git gerencia elas como marcadores para a último estado conhecido de onde essas branches estavam nos servidores.