Qual é a diferença entre HEAD ^ e HEAD ~ no Git?

Quando eu especifico um object ancestral commit no Git, estou confuso entre HEAD^ e HEAD~ .

Ambos têm uma versão “numerada” como HEAD^3 e HEAD~2 .

Eles parecem muito semelhantes ou iguais para mim, mas existem diferenças entre o til e o cursor?

HEAD^ significa o primeiro pai da ponta do ramo atual.

Lembre-se que commits git podem ter mais de um pai. HEAD^ é a abreviação de HEAD^1 e você também pode endereçar HEAD^2 e assim por diante, conforme apropriado.

Você pode chegar aos pais de qualquer commit, não apenas HEAD . Você também pode voltar através de gerações: por exemplo, master~2 significa o avô da ponta do ramo mestre, favorecendo o primeiro pai em casos de ambigüidade. Esses especificadores podem ser encadeados arbitrariamente, por exemplo , topic~3^2 .

Para os detalhes completos, consulte “Especificando revisões” na documentação do git rev-parse .

Para ter uma representação visual da ideia, vamos citar parte da documentação:

Aqui está uma ilustração, de Jon Loeliger. Ambos os nós de confirmação B e C são pais do nó de confirmação A. As confirmações de pai são ordenadas da esquerda para a direita.

 GHIJ \ / \ / DEF \ | / \ \ | / | \|/ | BC \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 

A diferença entre HEAD^ e HEAD~ é bem descrita pela ilustração (por Jon Loeliger) encontrada em http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html .

Esta documentação pode ser um pouco obscura para iniciantes, então eu reproduzi essa ilustração abaixo:

 GHIJ \ / \ / DEF \ | / \ \ | / | \|/ | BC \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 

Ambos ~ e ^ por si só referem-se ao pai do commit ( ~~ e ^^ ambos se referem ao avô cometem, etc.) Mas eles diferem em significado quando são usados ​​com números:

  • ~2 significa subir dois níveis na hierarquia , através do primeiro pai se um commit tiver mais de um pai

  • ^2 significa o segundo pai em que um commit tem mais de um pai (ou seja, porque é uma mesclagem)

Estes podem ser combinados, então HEAD~2^3 significa o terceiro commit pai do HEAD .

Meus dois centavos…

insira a descrição da imagem aqui

O formato ^ permite selecionar o enésimo pai do commit (relevante em fusões). O formato ~ permite que você selecione a enésima confirmação ancestral, sempre seguindo o primeiro pai. Veja a documentação do git-rev-parse para alguns exemplos.

Aqui está uma explicação muito boa tirada textualmente de http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :

ref~ é a abreviação de ref~1 e significa o primeiro pai do commit. ref~2 significa o primeiro pai da primeira mãe do commit. ref~3 significa o primeiro pai dos pais do primeiro pai do commit. E assim por diante.

ref^ é a abreviação de ref^1 e significa o primeiro pai do commit. Mas onde os dois diferem é que ref^2 significa o segundo pai do commit (lembre-se, os commits podem ter dois pais quando eles são uma mesclagem).

Os operadores ^ e ~ podem ser combinados.

insira a descrição da imagem aqui

Vale a pena notar que o git também tem uma syntax para rastrear “from-where-you-came” / “querer-voltar-agora” – por exemplo, HEAD@{1} fará referência ao local de onde você pulou para novo local de confirmação.

Basicamente, as variables HEAD@{} capturam a história do movimento HEAD, e você pode decidir usar uma determinada cabeça observando os reflexos do git usando o comando git reflog .

Exemplo:

 0aee51f HEAD@{0}: reset: moving to HEAD@{5} 290e035 HEAD@{1}: reset: moving to HEAD@{7} 0aee51f HEAD@{2}: reset: moving to HEAD@{3} 290e035 HEAD@{3}: reset: moving to HEAD@{3} 9e77426 HEAD@{4}: reset: moving to HEAD@{3} 290e035 HEAD@{5}: reset: moving to HEAD@{3} 0aee51f HEAD@{6}: reset: moving to HEAD@{3} 290e035 HEAD@{7}: reset: moving to HEAD@{3} 9e77426 HEAD@{8}: reset: moving to HEAD@{3} 290e035 HEAD@{9}: reset: moving to HEAD@{1} 0aee51f HEAD@{10}: reset: moving to HEAD@{4} 290e035 HEAD@{11}: reset: moving to HEAD^ 9e77426 HEAD@{12}: reset: moving to HEAD^ eb48179 HEAD@{13}: reset: moving to HEAD~ f916d93 HEAD@{14}: reset: moving to HEAD~ 0aee51f HEAD@{15}: reset: moving to HEAD@{5} f19fd9b HEAD@{16}: reset: moving to HEAD~1 290e035 HEAD@{17}: reset: moving to HEAD~2 eb48179 HEAD@{18}: reset: moving to HEAD~2 0aee51f HEAD@{19}: reset: moving to HEAD@{5} eb48179 HEAD@{20}: reset: moving to HEAD~2 0aee51f HEAD@{21}: reset: moving to HEAD@{1} f916d93 HEAD@{22}: reset: moving to HEAD@{1} 0aee51f HEAD@{23}: reset: moving to HEAD@{1} f916d93 HEAD@{24}: reset: moving to HEAD^ 0aee51f HEAD@{25}: commit (amend): 3rd commmit 35a7332 HEAD@{26}: checkout: moving from temp2_new_br to temp2_new_br 35a7332 HEAD@{27}: commit (amend): 3rd commmit 72c0be8 HEAD@{28}: commit (amend): 3rd commmit 

Um exemplo poderia ser que eu fiz local-commits a-> b-> c-> d e depois voltei a descartar 2 commits para checar meu código – git reset HEAD~2 – e depois disso eu quero mover minha HEAD de volta para d – git reset HEAD@{1} .

HEAD ^^^ é o mesmo que HEAD ~ 3, selecionando o terceiro commit antes de HEAD

HEAD ^ 2 especifica o segundo header em uma consolidação de mesclagem

  • HEAD ~ especifica o primeiro pai em um “branch”

  • HEAD ^ permite que você selecione um pai específico do commit

Um exemplo:

Se você quiser seguir um ramo lateral, você tem que especificar algo como

 master~209^2~15 

Simplisticamente :

  • ~ especifica ancestrais
  • ^ especifica pais

Você pode especificar uma ou mais ramificações ao mesclar. Em seguida, o commit tem dois ou mais pais e, em seguida, ^ é útil para indicar os pais.

Suponha que você esteja no ramo A e tenha mais dois ramos: B e C.

Em cada ramo, três últimos commits são respectivamente:

  • A : A1 , A2 , A3
  • B : B1 , B2 , B3
  • C : C1 , C3 , C3

Se agora na ramificação A você executar o comando:

 git merge BC 

então você está combinando três ramificações juntas (aqui seu commit de mesclagem tem três pais)

e

~ indica o n’ésimo ancestral no primeiro ramo, então

  • HEAD~ indica A3
  • HEAD~2 indica A2
  • HEAD~3 indica A1

^ indica o n’th pai, então

  • HEAD^ indica A3
  • HEAD^2 indica B3
  • HEAD^3 indica C3

O próximo uso de ~ ou ^ ao lado um do outro, está no contexto do commit designado pelos caracteres anteriores.

Aviso 1 :

  • HEAD~3 é sempre igual a: HEAD~~~ e: HEAD^^^ (cada indica A1 ),

e geralmente :

  • HEAD~n é sempre igual a: HEAD~...~ ( n vezes ~ ) e para: HEAD^...^ ( n vezes ^ ).

Aviso 2 :

  • HEAD^3 não é o mesmo que HEAD^^^ (o primeiro indica C3 e o segundo indica A1 ),

e geralmente :

  • HEAD^1 é o mesmo que HEAD^ ,
  • mas para n > 1: HEAD^n é sempre diferente de HEAD^...^ ( n vezes ~ ).

TLDR

~ é o que você quer na maior parte do tempo, ele faz referência a commits passados ​​no branch atual

references referencia pais (o git-merge cria um segundo pai ou mais)

A ~ é sempre o mesmo que A ^
A ~~ é sempre o mesmo que A ^^, e assim por diante
A ~ 2 não é o mesmo que A ^ 2, no entanto,
porque ~ 2 é uma abreviação para ~~
enquanto ^ 2 não é uma abreviação para nada, significa o segundo pai

exemplo real da diferença entre HEAD ~ e HEAD ^

CABEÇA ^ VS CABEÇA ~

Simplificando, para o primeiro nível de parentesco (ancestralidade, inheritance, linhagem, etc.) HEAD ^ e HEAD ~ ambos apontam para o mesmo commit, que é (localizado) um pai acima de HEAD (commit).

Além disso, HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1. Mas HEAD ^^! = HEAD ^ 2! = HEAD ~ 2. Ainda HEAD ^^ = HEAD ~ 2. Leia.

Além do primeiro nível de parentesco, as coisas ficam mais complicadas, especialmente se o ramo / ramificação mestre tiver mesclado (de outros ramos). Há também a questão da syntax com o cursor, HEAD ^^ = HEAD ~ 2 (eles são equivalentes) MAS HEAD ^^! = HEAD ^ 2 (são duas coisas totalmente diferentes).

Cada / o cursor se refere ao primeiro pai do HEAD, e é por isso que os compassos agrupados são equivalentes às expressões de til, porque se referem aos primeiros pais do primeiro pai, etc., etc. estritamente baseados no número dos carets conectados ou no número seguinte ao til (de qualquer forma, ambos significam a mesma coisa), ou seja, ficar com o primeiro pai e subir x gerações.

HEAD ~ 2 (ou HEAD ^^) refere-se ao commit que é dois níveis de ascendência acima / acima do commit atual (o HEAD) na hierarquia, significando o commit do avô do HEAD.

HEAD ^ 2, por outro lado, refere-se NÃO à segunda confirmação do pai do primeiro pai, mas simplesmente à segunda confirmação do pai. Isso ocorre porque o cursor significa o pai da confirmação e o número a seguir significa a qual / qual compromisso pai é referido (o primeiro pai, no caso em que o cursor não é seguido por um número [porque é uma abreviação para o número sendo 1, significando o primeiro pai]). Ao contrário do cursor, o número que se segue depois não implica em outro nível hierárquico para cima, mas implica quantos níveis de lado, na hierarquia, é preciso encontrar o pai correto (commit). Ao contrário do número em uma expressão de til, é apenas um dos pais na hierarquia, independentemente do número (imediatamente) que continua o cursor. Em vez de ascendente, o número à direita do cursor é considerado lateral para os pais de toda a hierarquia [em um nível de pais para cima que é equivalente ao número de carets consecutivos].

Então HEAD ^ 3 é igual ao terceiro pai do HEAD commit (NÃO o bisavô, que é o que HEAD ^^^ E HEAD ~ 3 seria …).