¿Cuál es la diferencia entre HEAD ^ y HEAD ~ en Git?


756

Cuando especifico un objeto de confirmación de ancestro en Git, estoy confundido entre HEAD^y HEAD~.

Ambos tienen una versión "numerada" como HEAD^3y HEAD~2.

Me parecen muy similares o iguales, pero ¿hay alguna diferencia entre la tilde y el caret?


64
es malo pegar enlaces, lo sé, pero esta es la mejor explicación que encontré y hay una imagen en ella. paulboxley.com/blog/2011/06/git-caret-and-tilde
igor

44
Los enlaces son especialmente malos cuando están rotos. Esa es la razón por la cual es más seguro responder la pregunta que ayuda a prevenir esto debido a la capacidad de copiar y pegar algunas explicaciones :)
Samuel

Respuestas:


763

Reglas de juego

  • Use la ~mayor parte del tiempo: para retroceder varias generaciones, generalmente lo que desea
  • Usar ^en confirmaciones de combinación, porque tienen dos o más padres (inmediatos)

Mnemotécnica:

  • Tilde ~es casi lineal en apariencia y quiere ir hacia atrás en línea recta.
  • Caret ^sugiere un segmento interesante de un árbol o una bifurcación en el camino

Tilde

La sección "Especificación de revisiones" de la git rev-parsedocumentación define ~como

<rev>~<n>, Por ejemplomaster~3
un sufijo ~<n>a un parámetro de revisión significa que el objeto que es el commit n º antepasado generación del llamado cometer objeto, después de sólo la primera padres. Por ejemplo, <rev>~3es equivalente a lo <rev>^^^que es equivalente a <rev>^1^1^1...

Puede llegar a los padres de cualquier compromiso, no solo HEAD. También puede retroceder de generación en generación: por ejemplo, master~2significa el abuelo de la punta de la rama maestra, favoreciendo al primer padre en los compromisos de fusión.

Signo de intercalación

El historial de Git es no lineal: un gráfico acíclico dirigido (DAG) o un árbol. Para un compromiso con un solo padre, rev~y rev^significa lo mismo. El selector de intercalación se vuelve útil con los compromisos de fusión porque cada uno es hijo de dos o más padres, y daña el lenguaje tomado de la biología.

HEAD^significa el primer padre inmediato de la punta de la rama actual. HEAD^es la abreviatura de HEAD^1, y también puede abordar, HEAD^2etc., según corresponda. La misma sección de la git rev-parsedocumentación lo define como

<rev>^, por ejemplo HEAD^ ,v1.5.1^0
un sufijo ^para un parámetro de revisión significa el primer padre de ese objeto de confirmación. ^<n>significa que el n º padre ([ por ejemplo ] <rev>^es equivalente a <rev>^1). Como regla especial, <rev>^0significa el compromiso en sí y se usa cuando <rev>es el nombre del objeto de una etiqueta que se refiere a un objeto de compromiso.

Ejemplos

Estos especificadores o selectores se pueden encadenar arbitrariamente, por ejemplo , topic~3^2en inglés es el segundo padre de la confirmación de fusión que es el bisabuelo (tres generaciones atrás) de la punta actual de la rama topic.

La sección de la git rev-parsedocumentación mencionada anteriormente traza muchos caminos a través de una historia git nocional. El tiempo fluye generalmente hacia abajo. Los commits D, F, B y A son commits de fusión.

Aquí hay una ilustración, de Jon Loeliger. Ambos nodos de confirmación B y C son padres del nodo de confirmación A. Las confirmaciones principales se ordenan de izquierda a derecha.

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A

A =      = A^0
B = A^   = A^1     = A~1
C = 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

Ejecute el siguiente código para crear un repositorio git cuyo historial coincida con la ilustración citada.

#! /usr/bin/env perl

use strict;
use warnings;
use subs qw/ postorder /;
use File::Temp qw/ mkdtemp /;

my %sha1;
my %parents = (
  A => [ qw/ B C /               ],
  B => [ qw/     D E F /         ],
  C => [ qw/         F /         ],
  D => [ qw/           G H /     ],
  F => [ qw/               I J / ],
);

sub postorder {
  my($root,$hash) = @_;
  my @parents = @{ $parents{$root} || [] };
  postorder($_, $hash) for @parents;
  return if $sha1{$root};
  @parents = map "-p $sha1{$_}", @parents;
  chomp($sha1{$root} = `git commit-tree @parents -m "$root" $hash`);
  die "$0: git commit-tree failed" if $?;
  system("git tag -a -m '$sha1{$root}' '$root' '$sha1{$root}'") == 0 or die "$0: git tag failed";
}

$0 =~ s!^.*/!!;  # / fix Stack Overflow highlighting
my $repo = mkdtemp "repoXXXXXXXX";
chdir $repo or die "$0: chdir: $!";
system("git init") == 0               or die "$0: git init failed";
chomp(my $tree = `git write-tree`);      die "$0: git write-tree failed" if $?;

postorder 'A', $tree;
system "git update-ref HEAD   $sha1{A}"; die "$0: git update-ref failed" if $?;
system "git update-ref master $sha1{A}"; die "$0: git update-ref failed" if $?;

# for browsing history - http://blog.kfish.org/2010/04/git-lola.html
system "git config alias.lol  'log --graph --decorate --pretty=oneline --abbrev-commit'";
system "git config alias.lola 'log --graph --decorate --pretty=oneline --abbrev-commit --all'";

Se agrega alias de la nueva operación de usar y tirar sólo por git lolygit lola para que puedas ver la historia como en

$ git lol
*   29392c8 (HEAD -> master, tag: A) A
|\
| * a1ef6fd (tag: C) C
| |
|  \
*-. \   8ae20e9 (tag: B) B
|\ \ \
| | |/
| | *   03160db (tag: F) F
| | |\
| | | * 9df28cb (tag: J) J
| | * 2afd329 (tag: I) I
| * a77cb1f (tag: E) E
*   cd75703 (tag: D) D
|\
| * 3043d25 (tag: H) H
* 4ab0473 (tag: G) G

Tenga en cuenta que en su máquina los nombres de los objetos SHA-1 diferirán de los anteriores, pero las etiquetas le permiten abordar las confirmaciones por nombre y verificar su comprensión.

$ git log -1 --format=%f $(git rev-parse A^)
B
$ git log -1 --format=%f $(git rev-parse A~^3~)
I
$ git log -1 --format=%f $(git rev-parse A^2~)
F

La sección "Especificación de revisiones" en la git rev-parsedocumentación está llena de gran información y merece una lectura en profundidad. Vea también Herramientas Git - Selección de revisión del libro Pro Git .

Orden de los padres se comprometen

El commit 89e4fcb0dd del propio historial de git es un commit de fusión, como se git show 89e4fcb0ddindica con la línea de encabezado Merge que muestra los nombres de objeto de los antepasados ​​inmediatos.

commit 89e4fcb0dd01b42e82b8f27f9a575111a26844df
Merge: c670b1f876 649bf3a42f b67d40adbb
Author: Junio C Hamano <gitster@pobox.com>
Date:   Mon Oct 29 10:15:31 2018 +0900

    Merge branches 'bp/reset-quiet' and 'js/mingw-http-ssl' into nd/config-split […]

Podemos confirmar el pedido pidiendo git rev-parseque se muestren en secuencia los padres inmediatos de 89e4fcb0dd.

$ git rev-parse 89e4fcb0dd^1 89e4fcb0dd^2 89e4fcb0dd^3
c670b1f876521c9f7cd40184bf7ed05aad843433
649bf3a42f344e71b1b5a7f562576f911a1f7423
b67d40adbbaf4f5c4898001bf062a9fd67e43368

Consultar al cuarto padre inexistente da como resultado un error.

$ git rev-parse 89e4fcb0dd^4
89e4fcb0dd^4
fatal: ambiguous argument '89e4fcb0dd^4': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Si solo desea extraer los padres, use un formato bonito %P para los hashes completos

$ git log -1 --pretty=%P 89e4fcb0dd
c670b1f876521c9f7cd40184bf7ed05aad843433 649bf3a42f344e71b1b5a7f562576f911a1f7423 b67d40adbbaf4f5c4898001bf062a9fd67e43368

o %ppara padres abreviados.

$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb

parece que ^ puede manejar todos los casos y uno puede preguntarse por qué ~ apareció en primer lugar. ¿Por qué no recordar solo cómo funciona ^?
Sbu

esto es súper confuso aún ... suponiendo que G sea HEAD, así que si hago un HEAD ^ eso sería D ... ¿verdad?
Patoshi パ ト シ

12
@duckx el gráfico en realidad va de arriba a abajo, por lo que A es la confirmación más reciente y G es una de las más antiguas. El camino de G a D es hacia adelante, no hacia atrás, por lo que puedo decir.
goldenratio

@SimonBudin, supongo, no es muy conveniente de usar en ^^^^^^^lugar de ~7, ¿verdad? Por eso ~es útil
YakovL

1
@AdityaVikasDevarapalli Eso sería bueno como su propia pregunta.
Greg Bacon

340

La diferencia entre HEAD^y HEAD~está bien descrita en la ilustración (por Jon Loeliger) que se encuentra en http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html .

Esta documentación puede ser un poco oscura para los principiantes, así que he reproducido la siguiente ilustración:

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A
A =      = A^0
B = A^   = A^1     = A~1
C = 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

12
Solo una pregunta. ¿Cómo es posible que un compromiso tenga más de dos padres? (Ver B: sus padres son D, E y F) Me imagino que la única forma en que un commit puede tener dos padres es cuando es un commit de fusión ... pero ¿cómo puedes combinar 3 commits al mismo tiempo?
tsikov

Si no me equivoco, esto puede ser obvio, pero creo que debería especificarse que HEAD ~ sigue la rama actual (como Diego Dias se menciona a continuación).
fibono

2
Además, F = A^2^.
Mateen Ulhaq

2
Por lo tanto, ^ == ^1 == LEFTMOST PARENT, ^2 == SECOND LEFTMOST PARENTy así sucesivamente. Y ~ == ~1 == LEFTMOST PARENT, ~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT. Por extensión,~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
Alexander Torstling

1
@AlexanderTorstling esto fue muy útil para mí. Sin embargo, ¿qué significa izquierda y derecha aquí?
polynomial_donut

287

Ambos ~y ^por sí solos se refieren al padre del compromiso ( ~~y ^^ambos se refieren al compromiso de los abuelos, etc.) Pero difieren en significado cuando se usan con números:

  • ~2significa dos niveles en la jerarquía , a través del primer padre si una confirmación tiene más de un padre

  • ^2significa el segundo padre donde un commit tiene más de un padre (es decir, porque es una fusión)

Estos se pueden combinar, por lo que HEAD~2^3significa HEADel compromiso del tercer padre del compromiso del abuelo.


2
Leer esto seguido de la imagen de stackoverflow.com/questions/2221658/… tenía mucho sentido.
kunigami

23
Esta debería ser la respuesta aceptada, mucho más sucinta y útil que las demás.
RichVel

3
¡Esta respuesta me hizo distinguir entre caret / tilde sin número y con número! Pensé que ^^era lo mismo ^2pero no lo es.
Alexander Derck el

278

Mis dos centavos...

ingrese la descripción de la imagen aquí


Y cómo H=A~2^2no H=A~2^1?
Mohammad Faisal

3
Si hubiera figurado a cabo correctamente, las confirmaciones A, B, D, Gestán en la misma rama y el envío de datos Des una fusión de Gy H, por lo tanto, tener dos padres. Entonces el commit ( H) de otra rama es referenciado por ^2.
Mohammad Faisal

62

Aquí hay una muy buena explicación tomada textualmente de http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :

ref~es la abreviatura de ref~1y significa el primer padre del commit. ref~2significa el primer padre del primer padre del commit. ref~3significa el primer padre del primer padre del commit. Y así.

ref^es la abreviatura de ref^1y significa el primer padre del commit. Pero donde los dos difieren es que ref^2significa el segundo padre del commit (recuerde, los commits pueden tener dos padres cuando son una fusión).

Los operadores ^y ~se pueden combinar.

ingrese la descripción de la imagen aquí


55
Gracias por explicar las diferencias en lugar de publicar una gran cantidad de ejemplos.
Kirk Broadhurst

32

El ^<n>formato le permite seleccionar el enésimo padre de la confirmación (relevante en las fusiones). El ~<n>formato le permite seleccionar la enésima confirmación de antepasado, siempre siguiendo al primer padre. Consulte la documentación de git-rev-parse para ver algunos ejemplos.


21

Vale la pena señalar que git también tiene una sintaxis para rastrear "de donde viniste" / "querer volver ahora", por ejemplo, HEAD@{1}hará referencia al lugar desde donde saltaste a la nueva ubicación de confirmación.

Básicamente, las HEAD@{}variables capturan la historia del movimiento de HEAD, y puede decidir usar una cabeza en particular buscando en los registros de git usando el comando git reflog.

Ejemplo:

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

Un ejemplo podría ser que hice commits locales a-> b-> c-> d y luego volví descartando 2 commits para verificar mi código git reset HEAD~2- y luego quiero mover mi HEAD de nuevo a d - git reset HEAD@{1}.


17

Simplista :

  • ~ especifica antepasados
  • ^ especifica padres

Puede especificar una o más ramas al fusionar. Entonces un commit tiene dos o más padres y luego ^es útil para indicar padres.

Supongamos que usted está en la rama A y tiene dos ramas más: B y C .

En cada rama las tres últimas confirmaciones son:

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

Si ahora en la rama A ejecuta el comando:

git merge B C

entonces está combinando tres ramas juntas (aquí su compromiso de fusión tiene tres padres)

y

~ indica el enésimo antepasado en la primera rama, entonces

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

^ indica el enésimo padre, entonces

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

El siguiente uso de ~o ^uno al lado del otro es en el contexto del compromiso designado por los caracteres anteriores.

Aviso 1 :

  • HEAD~3siempre es igual a: HEAD~~~y a: HEAD^^^(cada indica A1 ),

        y en general :

  • HEAD~nsiempre es igual a: HEAD~...~( n veces ~) y a: HEAD^...^( n veces ^).

Aviso 2 :

  • HEAD^3no es lo mismo que HEAD^^^(el primero indica C3 y el segundo indica A1 ),

        y en general :

  • HEAD^1es la misma que HEAD^,
  • pero para n > 1: noHEAD^n siempre es lo mismo que HEAD^...^( n veces ~).

15

TLDR

~ es lo que quieres la mayor parte del tiempo, hace referencia a confirmaciones anteriores a la rama actual

^ referencias padres (git-merge crea un segundo padre o más)

A ~ es siempre lo mismo que A ^
A ~~ es siempre lo mismo que A ^^, y así sucesivamente
A ~ 2 no es lo mismo que A ^ 2,
porque ~ 2 es una abreviatura de ~~
mientras que ^ 2 no lo es abreviatura de cualquier cosa, significa el segundo padre


11

HEAD ^^^ es lo mismo que HEAD ~ 3, seleccionando el tercer commit antes de HEAD

HEAD ^ 2 especifica la segunda cabeza en un commit de fusión


9
  • HEAD ~ especifica el primer padre en una "rama"

  • HEAD ^ le permite seleccionar un padre específico de la confirmación

Un ejemplo:

Si desea seguir una rama lateral, debe especificar algo como

master~209^2~15


0

En pocas palabras, para el primer nivel de parentesco (ascendencia, herencia, linaje, etc.) HEAD ^ y HEAD ~ ambos apuntan al mismo commit, que está (ubicado) un padre encima del HEAD (commit).

Además, HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1. Pero HEAD ^^! = HEAD ^ 2! = HEAD ~ 2. Sin embargo, HEAD ^^ = HEAD ~ 2. Sigue leyendo.

Más allá del primer nivel de paternidad, las cosas se ponen más complicadas, especialmente si la rama de trabajo / rama maestra ha tenido fusiones (de otras ramas). También está la cuestión de la sintaxis con el cursor, HEAD ^^ = HEAD ~ 2 (son equivalentes) PERO HEAD ^^! = HEAD ^ 2 (son dos cosas completamente diferentes).

Cada / el cursor se refiere al primer padre de la CABEZA, razón por la cual los cuidados unidos entre sí son equivalentes a las expresiones tilde, porque se refieren a los primeros padres del primer padre (primer padre), etc., etc., basados ​​estrictamente en el número de cuidados conectados o en el número que sigue a la tilde (de cualquier manera, ambos significan lo mismo), es decir, quedarse con el primer padre y subir x generaciones.

HEAD ~ 2 (o HEAD ^^) se refiere al commit que es dos niveles de ascendencia arriba / arriba del commit actual (HEAD) en la jerarquía, lo que significa el commit de los abuelos de HEAD.

HEAD ^ 2, por otro lado, no se refiere al commit del segundo padre del primer padre, sino simplemente al commit del segundo padre. Esto se debe a que el símbolo de intercalación significa el padre del compromiso, y el número siguiente significa a qué / qué compromiso primario se refiere (el primer padre, en el caso de que el número no siga al símbolo de intercalación [porque es la abreviatura del número siendo 1, que significa el primer padre]). A diferencia del símbolo de intercalación, el número que sigue después no implica otro nivel de jerarquía hacia arriba, sino que implica cuántos niveles de lado, dentro de la jerarquía, uno necesita ir a buscar el padre correcto (commit). A diferencia del número en una expresión tilde, solo está un padre arriba en la jerarquía, independientemente del número (inmediatamente) que procede del cursor. En lugar de hacia arriba, la preocupación

Entonces HEAD ^ 3 es igual al tercer padre de HEAD commit (NO el bisabuelo, que es lo que HEAD ^^^ Y HEAD ~ 3 serían ...).


-1

~ Esto significa padre.

^ si tiene padres de dos o más, como merge commit, podemos seleccionar el segundo padre u otro.

así que si solo una cosa como (HEAD ~ o HEAD ^), tiene los mismos resultados.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.