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