Apenas complementando sobre HEAD^
e HEAD~
. É verdade que HEAD^^
é equivalente a HEAD~2
, mas na verdade existe uma diferença mais fundamental sobre o funcionamento deles.
Para explicar a diferença, temos que lembrar que um commit pode ter mais de um pai, quando este é o resultado de um merge que não teve fast-forward.
O mais comum é quando eu faço git merge branch
e não há fast-forward, pois aí o resultado é um commit com dois pais: o commit para onde o HEAD
apontava e o commit para onde o branch
aponta. Mas nada impede que eu faça algo como git merge branch1 branch2 branch3
, e neste caso o commit resultante poderá ter até quatro pais (caso não seja possível fazer fast-forward em nenhum dos branches).
E é aí que usar ^
ou ~
começa a fazer diferença. Pois o ^N
é usado para obter o enésimo pai, enquanto que ~N
é usado para o enésimo ancestral.
Para entender melhor, segue um exemplo retirado da documentação oficial:
G H I J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A
No caso, o commit A
tem dois pais: B
e C
. O commit B
tem 3 pais: D
, E
e F
, e assim por diante.
Se eu fizer A^
, A^1
ou A~1
, o resultado é B
. Agora, como eu chego em C
a partir de A
? Neste caso, eu uso A^2
, ou seja, o "segundo pai de A
".
Já se eu fizer A~2
ou A^^
(ou ainda A^1^1
), eu chego em D
. E para chegar em E
, tenho que fazer algo como B^2
(o segundo pai de B
), ou ainda A^^2
(pois A^
equivale a B
, e depois com ^2
eu chego em E
).
Essa é a diferença: A^2
é o segundo pai de A
, enquanto A~2
é o ancestral de A
"voltando duas gerações" (e considerando sempre o primeiro pai de cada geração).
Ainda usando o mesmo exemplo: para chegar em F
, podemos fazer B^3
(o terceiro pai de B
), ou A^^3
(o terceiro pai do primeiro pai de A
). Para chegar em H
, posso fazer algo como A~2^2
(pois com A~2
eu chego em D
, e depois ^2
pega o segundo pai de D
, que é H
).
Enfim, esses atalhos existem porque fazem coisas diferentes: ^
olha apenas uma geração acima, e procura pelo enésimo pai (ou o primeiro, se nenhum número for especificado). Já o ~
vai subindo na "árvore genealógica", podendo olhar várias gerações anteriores.
O fato de A^^
ser equivalente a A~2
é mera consequência desta definição. Mas o intuito original não foi um ser atalho para o outro.