A dica Git desta semana é sobre a procura de commits com a sintaxe de revisões git.
Git Log
Git log é uma ferramenta versátil que permite que você realize a
introspecção do estado de seu repositório. Já estávamos usando-o
implicitamente em uma série de outros exemplos; neste artigo, vamos
examinar algumas das outras opções que o git log aborda, bem como as maneiras pelas quais podemos nos referir a itens no histórico do Git.
Na semana passada, examinamos reflogs do Git, que (junto com o artigo anterior sobre git stash) discutiu uma notação para olhar para commits com a sintaxe HEAD@{1} (cf stash@{1}).
Na verdade, há um monte de outros mecanismos que podemos usar para nos
referirmos a itens do histórico do Git, além do hash do commit ou nome
do branch.
Um histórico do git é um grafo acíclico dirigido de commits, a partir
do HEAD oposto para uma (ou mais) raízes. Na maioria dos casos, commits
possuem um único pai, mas merge commits possuem dois (ou mais) pais.
(Hg, pelo contrário, só pode ter um ou dois pais. A conversão de um
repositório Git para um repositório Hg, portanto, não é totalmente
fiel.)
Uma vez que cada commit pode ter mais de um pai, o operador pai (^ como um sufixo) permite que você elimine a ambiguidade de qual pai você está se referindo. Dado que cada merge node é representado como um par (ou mais) de commits, os pais são numerados de 1 a n. (O número 0 especial é usado para se referir a si mesmo.)
# Dummy repository $ git log --oneline 77bc990 Third commit 25d4fc4 Second commit f0faab6 First commit $ git log --oneline HEAD^ 25d4fc4 Second commit f0faab6 First commit $ git log --oneline HEAD^ 25d4fc4 Second commit f0faab6 First commit $ git log --oneline HEAD^^ f0faab6 First commit $ git log --oneline HEAD^2 fatal: ambiguous argument 'HEAD^2': unknown revision or path not in the working tree.
Aqui, HEAD está apontando para 77bc990 Third commit, e assim ambos HEAD^ e HEAD^1 referem-se ao mesmo item (25d4fc4 Second commit). No entanto, HEAD^^ dá uma resposta diferente para HEAD^2; no primeiro, encontrando a raiz do histórico e no último dando um erro.
Isso é porque HEAD^^ significa "o (primeiro) pai do (primeiro) pai de HEAD", enquanto HEAD^2 significa "segundo pai do HEAD". Geralmente, HEAD^n, onde n >= 2 só faz sentido em merge nodes.
No entanto, há outra referência útil, o seletor avô. Em vez de considerar uma pesquisa baseada em amplitude, ele faz uma pesquisa baseada na profundidade:
# Dummy repository $ git log --oneline 77bc990 Third commit 25d4fc4 Second commit f0faab6 First commit $ git log --oneline HEAD~ 25d4fc4 Second commit f0faab6 First commit $ git log --oneline HEAD~~ f0faab6 First commit $ git log --oneline HEAD~2 f0faab6 First commit
Como o operador pai, o operador avô também pode ter um número, mas em vez de se referir ao enésimo pai, HEAD~n refere-se ao enésimo avô.
Note que o ~ seleciona até o primeiro pai (muito parecido com o que o
^ faz), mas as duas formas podem ser misturadas, se necessário. Nesse
caso, HEAD^~ e HEAD~^ têm o mesmo efeito, mas você pode explicitamente selecionar o item que quiser com um seletor numérico; por exemplo, git log HEAD^2~10 dá a você o décimo segundo pai do node merge do ancestral atual.
Intervalos e conjuntos
Bem como as referências individuais, também é possível fazer referência a gamas de Git. Na verdade, elas são conhecidas como conjuntos de commits (uma vez que não podem ser adjacentes na commit tree). A forma mais comum é mostrar os commits entre uma ref e outra:
$ git checkout -b other f0faab6 Switched to a new branch 'other' $ touch file $ git add file $ git commit -m "Adding file" file $ git log --oneline 1762164 Adding file f0faab6 First commit $ git log --oneline other..master 77bc990 Third commit 25d4fc4 Second commit $ git log --oneline master..other 1762164 Adding file
A sintaxe é between..and, quando ambas as referências podem
ser tanto uma das referências simbólicas do branch/tag ou um hash do
commit etc. O que isso está dizendo é "Mostre-me todos os commits que
estão em master, mas não em other" (e vice-versa, para o segundo).
No entanto, enquanto ele se parece com um intervalo, na verdade isso é apenas uma seleção set. A sintaxe between..and é na verdade uma abreviação para and --not between. Em outras palavras, o código acima está mostrando o que está em other, mas não em master.
Qual é a utilização do por extenso, quando a abreviação é muito mais
conveniente? Bem, o por extenso permite que você especifique mais de
duas referências. Por exemplo:
# Show all unmerged changes between features 1,2,3 and master $ git log ^master feature1 feature2 feature3 # Show changes in hotfix and release branches, but not in master $ git log ^master hotfix release-1.0 release-1.1
Na maioria dos casos, a comparação assimétrica (usando dois commits) é provavelmente o que você deseja. É importante notar, finalmente, que há um diff simétrico (usando dois commits) - em vez de dois pontos, use três:
$ git log master...other 1762164 Adding file 77bc990 Third commit 25d4fc4 Second commit $ git log other...master 1762164 Adding file 77bc990 Third commit 25d4fc4 Second commit
Note que não há diferença na ordem dos commits, uma vez que eles estão ordenados por tempo (ou como você quiser ordená-los no git log). E, uma vez que é simétrico, o resultado é o mesmo, independentemente da ordem de operandos.
Resumo
O Git permite especificar commits individuais, bem como uma variedade
de commits utilizando um número de identificação de uma git revision, e
estes podem ser utilizados para descobrir o estado e as diferenças
entre trees. Neste artigo, nós abordamos o operador pai (^), o operador
ancestral (~), bem como os intervalos com diferenças simétricas e
assimétricas.
⁂
Texto original disponível em http://alblue.bandlem.com/2011/05/git-tip-of-week-git-revisions.html