Desenvolvimento

31 jul, 2012

Dica Git da Semana: entendendo o índice

Publicidade

Na dica Git da semana anterior, nós vimos a adição interativa, isto é, a capacidade de adicionar apenas partes de um arquivo como parte do commit.

Agora já é tempo de focarmos na área de teste de Git, que implicitamente usamos quando adicionamos as partes do arquivo. Esta é uma das principais diferenças entre Git e os outros sistemas de controle de versão – e muitas vezes as pessoas ficam confusas sobre a sua finalidade.

A área de preparação do Git permite congelar um estado de sua árvore de trabalho, de modo que o git commit subsequente pegue o estado congelado e utilize como o ponto de contato. Muitos sistemas de controle de versão só permitem que você congele no ponto de commit e por isso não têm esse estágio intermediário, e quando você estiver usando o Git, pela primeira vez, provavelmente usará o git add para ser imediatamente seguido por um git commit, ou mesmo a criação de um alias para fazer tudo.

Então, por que o Git tem o conceito de um índice? Bem, lembre-se que o Git usa arquivos de conteúdo endereçável; em outras palavras, quando você possui uma parte específica do conteúdo (como o arquivo vazio), ela tem sempre a mesma identidade – e69 .. 391 – qualquer que seja a forma em que o arquivo for chamado. O que acontece quando você executa git add é que o objeto é adicionado ao banco de dados do objeto. Bem como o objeto que está sendo adicionado, ele precisa de um cursor para apontar para ele, então, há uma árvore virtual, chamada índice, que contém uma árvore, que aponta para as partículas nelas contidas.

Quando você adiciona os arquivos (ou faz um rm), você realmente acaba modificando esta árvore que representa o que você gostaria de fazer em seguida. Quando você executa o commit, ele pega a árvore, cria um objeto válido dela e faz o commit dela para o banco de dados (assim como a atualização do branch, se houver).

Embora possa não parecer útil ter este recurso (e alguns argumentam que este é um exemplo da complexidade do Git sobre outros sistemas), pode ser muito benéfico fazer operações específicas, como por e

  • Organizando as partes de um arquivo para dividí-lo em commits diferentes (como da última vez);
  • Trabalhe com grandes merges, onde muitos arquivos podem ter conflitos (você pode gravar os que não possuem conflitos e aqueles com os quais você já trabalhou, mas executando o git add; assim, sobrou um número menor de diferenças para processar). 

Quando você executar o git status, ele te dirá tudo que você precisa saber sobre como o índice corresponde à sua árvore de trabalho atual, dando-lhe diferentes mensagens sobre os arquivos que ele encontra. Para acelerar o processamento, o Git geralmente usa timestamps para determinar se um arquivo foi alterado. Mas fazendo uma varredura completa de processamento, ele irá calcular o hash SHA1 do conteúdo dos arquivos (e, portanto, os diretórios) para determinar as diferenças em relação ao índice:

(master) $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: deleteme
# renamed: same -> renamed
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: changed
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# addme

O conteúdo do índice é realmente o snapshot dos arquivos que foram modificados. Por exemplo, o arquivo renomeado e apagado acima sofreu alterações que foram encenadas (ou seja, adicionada ao índice), enquanto as mudanças não encenadas foram modificadas, mas ainda não estão comprometidos. O índice também permite a rápida identificação de alterações no repositório local, que ainda têm de ser adicionadas.

Assim, para um determinado arquivo, há possivelmente três cópias separadas. Há a versão anterior que foi commitada (HEAD, por exemplo), há a versão atual no disco (uma árvore de trabalho) e uma terceira cópia, que é uma versão em cache no índice. É por isso que, quando você tem uma mudança local combinada com uma que já existe, você pode ver o mesmo arquivo duas vezes na mensagem de status:

(master) apple[example] $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: three
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: three
#

Você pode usar git diff para mostrar as diferenças entre os dois arquivos:


(master) $ git diff
diff --git a/three b/three
index bb87574..3179466 100644
--- a/three
+++ b/three
@@ -1 +1 @@
-This is the version in the index
+This is the version in the working tree
(master) $ git diff --cached
diff --git a/three b/three
index 48d0444..bb87574 100644
--- a/three
+++ b/three
@@ -1 +1 @@
-This is the previously committed version
+This is the version in the index

No segundo exemplo, o — cached diz para comparar entre o índice e o commit anterior (caso contrário, ele estará comparando o índice e a árvore de trabalho). Você pode, se quiser, obter o conteúdo completo de cada um desses arquivos, desde que você conheça os hashes (mostrados acima nos diffs):

(master) $ git show 48d0444
This is the previously committed version
(master) $ git show bb87574
This is the version in the index
(master) $ cat three
This is the version in the working tree

O último, é claro, ainda não tem um objeto Git em virtude de nós não o termos adicionado. Se fôssemos adicioná-lo, substituiríamos a versão anterior no índice. Vamos dar um mergulho mais profundo no índice da próxima vez.

***

Artigo original disponível em: http://alblue.bandlem.com/2011/10/git-tip-of-week-understanding-index.html