Desenvolvimento

3 jul, 2012

Dica Git da semana: Detached Heads

Publicidade

A dica git da semana é sobre detached heads. Não é tão ruim quanto parece, e não envolve uma guilhotina.

Heads

Uma vez que um repositório git é uma árvore de commits, com cada commit apontando para o seu ancestral (is), é possível tratar diretamente um único commit por meio de seu hash de commit. Não só isso, mas é possível gravar esse hash em uma variedade de sistemas diferentes: Twitter, e-mail, Bugzilla etc.

Ambos Git branches e Git tags são apenas links para um item, por hash de commit, no repositório. Criar uma centena de tags (ou branches) equivale a criar uma centena de ponteiros, e é uma das razões pelas quais o Git é tão incrivelmente rápido.

Enquanto tags são (geralmente) imutáveis, branches não são. Cada vez que um commit é feito em um branch, o ponteiro (referência) é atualizado para apontar para o commit mais recente. Assim, três commits no branch envolvem três modificações do branch pointer (bem como as entradas correspondentes sendo adicionadas no repositório para o conteúdo).

Estes ponteiros são armazenados nos subdiretórios .git/refs. Tags são armazenadas em .git/refs/tags e branches são armazenados em .git/refs/heads. Se você olhar para qualquer um dos arquivos, vai encontrar cada tag correspondendo a um único arquivo, com 40 caracteres de hash de commit.

O ponto de partida para este artigo é que os branches são também conhecidos como heads. Quando você possui um branch master, há um arquivo refs/heads/master, que é um ponteiro para onde o branch atual está nesse ponto.

Detached heads

Assim, se um head é sinônimo de um branch, o que é que faz um detached head? Bem, é simplesmente um hash de commit que não é apontado por uma tag ou por um branch. Assim, sempre que você tiver verificado um head não referenciado, você termina com um detached heads. Talvez um exemplo seja:

$ git init example
Initialized empty Git repository in example/.git
(master) $ touch file
(master) $ git add file
(master) $ git commit -m "Initial"
[master (root-commit) 123be6a] Initial
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file
(master) $ touch other
(master) $ git add other
(master) $ git commit -m "Second"
[master 5a11d1c] Second
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 other
(master) $ git log --oneline
5a11d1c Second
123be6a Initial
(master) $ git checkout HEAD^
Note: checking out 'HEAD^'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

HEAD is now at 123be6a... Initial
((123be6a...)) $ git log --oneline
123be6a Initial

O que fizemos aqui foi criar um repositório com dois commits, e, em seguida, voltamos uma versão. (As verificações HEAD^ conferem o pai de HEAD). O branch master continua apontando para o segundo commit (5a11d1c), mas nós estamos olhando para efetivamente commit sem nome.

A verificação do Git contém um .git/HEAD, que normalmente aponta (indiretamente) para um ref. No caso do modo “detached head”, o arquivo .git/HEAD contém o hash de commit:

((123be6a...)) $ cat .git/HEAD
123be6a76168aca712aea16076e971c23835f8ca
((123be6a...)) git checkout master
Previous HEAD position was 123be6a... Initial
Switched to branch 'master'
(master) $ cat .git/HEAD
ref: refs/heads/master

Trabalhar com (ou sobre) um detached head não é um problema. Isso ocorre quando você está lidando com bisects, ou se você quiser simplesmente verificar uma versão específica de um commit anterior. Não há nada para impedi-lo a trabalhar nesse branch sem nome, você pode continuar e comitar durante o tempo que quiser.

Tenha em mente, no entanto, que encontrar um commit é dependente do que você faz com aquele hash. Normalmente, você irá criar um novo branch (por exemplo, é uma correção de bug para um pouco de código previamente lançado), ou você acaba tagueando com um identificador hot fix. Há sempre o reflag que você pode usar para voltar para o seu commit se você acabar mudando de um detached heads. Basta lembrar que o git gc periódico será executado e irá limpar commits que não são referenciados (direta ou indiretamente) por uma tag ou um branch.

?

Texto original disponível em http://alblue.bandlem.com/2011/08/git-tip-of-week-detached-heads.html