Jaka jest różnica między HEAD ^ a HEAD ~ w Git?

755

Kiedy określam obiekt zatwierdzania przodka w Git, jestem mylony między HEAD^a HEAD~.

Oba mają wersję „numerowaną”, taką jak HEAD^3i HEAD~2.

Wydają mi się bardzo podobne lub takie same, ale czy są jakieś różnice między tyldą a karetką?

TK.
źródło
64
źle jest wklejać linki, wiem, ale to najlepsze wytłumaczenie, jakie znalazłem, i jest tam zdjęcie. paulboxley.com/blog/2011/06/git-caret-and-tilde
igor
4
Linki są szczególnie złe, gdy są zepsute. Dlatego bezpieczniej jest odpowiedzieć na pytanie, które pomaga temu zapobiec, ponieważ można skopiować kilka wyjaśnień :)
Samuel

Odpowiedzi:

762

Reguły kciuka

  • Wykorzystuj ~większość czasu - aby cofnąć się o kilka pokoleń, zwykle tego, czego chcesz
  • Użyj ^przy łączeniu zatwierdzeń - ponieważ mają dwóch lub więcej (bezpośrednich) rodziców

Mnemonika:

  • Tilde ~ma prawie liniowy wygląd i chce cofać się w linii prostej
  • Caret ^sugeruje interesujący fragment drzewa lub widelca na drodze

Tylda

W sekcji „Określanie poprawek” git rev-parsedokumentacji zdefiniowano ~jako

<rev>~<n>, npmaster~3
. Sufiks ~<n>parametru rewizji oznacza obiekt zatwierdzenia, który jest przodkiem nazwanego obiektu zatwierdzenia n- tej generacji, podążając tylko za pierwszymi rodzicami. Na przykład <rev>~3jest równoważne z tym, <rev>^^^co jest równoważne <rev>^1^1^1

Możesz dostać się do rodziców każdego zobowiązania, nie tylko HEAD. Możesz także cofać się o kolejne pokolenia: na przykład master~2oznacza dziadka z wierzchołka gałęzi master, faworyzując pierwszego rodzica przy zatwierdzaniu zmian.

Wstawka korektorska

Historia Git jest nieliniowa: ukierunkowany wykres acykliczny (DAG) lub drzewo. Dla zatwierdzenia tylko z jednym rodzicem rev~i rev^oznacza to samo. Selektor karetki przydaje się przy zatwierdzaniu fuzji, ponieważ każde z nich jest dzieckiem dwóch lub więcej rodziców - i napina język zapożyczony z biologii.

HEAD^oznacza pierwszego bezpośredniego rodzica wierzchołka bieżącej gałęzi. HEAD^jest skrótem od HEAD^1, i można również adresować HEAD^2i tak dalej, stosownie do przypadku. Ta sama sekcja git rev-parsedokumentacji definiuje go jako

<rev>^, Na przykład HEAD^ ,v1.5.1^0
przyrostek ^do zmiany parametru oznacza pierwszego macierzystego tego zestaw zmian. ^<n>oznacza n- tego rodzica ([ np. ] <rev>^jest równoważne <rev>^1). Jako specjalna zasada <rev>^0oznacza samo zatwierdzenie i jest używane, gdy <rev>jest nazwą obiektu znacznika, który odnosi się do obiektu zatwierdzenia.

Przykłady

Te Specyfikatory lub przełączniki mogą być dowolnie łączone, na przykład , topic~3^2w języku angielskim jest drugim rodzicem scaleniu popełnić że jest pra-dziadek (trzy pokolenia wstecz) bieżącego wierzchołka gałęzi topic.

Wyżej wspomniany rozdział git rev-parsedokumentacji śledzi wiele ścieżek w hipotetycznej historii git. Czas płynie zasadniczo w dół. Zatwierdzenia D, F, B i A są zatwierdzeniami scalania.

Oto ilustracja Jona Loeligera. Oba węzły zatwierdzania B i C są rodzicami węzła zatwierdzania A. Zatwierdzenia rodziców są uporządkowane od lewej do prawej.

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

Uruchom poniższy kod, aby utworzyć repozytorium git, którego historia pasuje do cytowanej ilustracji.

#! /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'";

Dodaje aliasy w nowym wyrzucenie repo tylko dla git loligit lola tak można zobaczyć, jak w historii

$ 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

Zauważ, że na twoim komputerze nazwy obiektów SHA-1 będą się różnić od powyższych, ale tagi pozwalają na adresowanie zatwierdzeń według nazwy i sprawdzanie zrozumienia.

$ 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

W „Określanie Rewizje” w git rev-parsedokumentacji jest pełen wspaniałych informacji i jest warta odczytu w głębi. Zobacz także Git Tools - Wybór wersji z książki Pro Git .

Kolejność zobowiązań nadrzędnych

Zatwierdzenie 89e4fcb0dd z własnej historii gita jest zatwierdzeniem scalenia, jak git show 89e4fcb0ddwskazuje wiersz nagłówka Scal, który wyświetla nazwy obiektów bezpośrednich przodków.

commit 89e4fcb0dd01b42e82b8f27f9a575111a26844df
Merge: c670b1f876 649bf3a42f b67d40adbb
Author: Junio C Hamano <[email protected]>
Date:   Mon Oct 29 10:15:31 2018 +0900

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

Możemy potwierdzić zamówienie, prosząc git rev-parseo pokazanie najbliższych rodziców 89e4fcb0dd.

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

Zapytanie o nieistniejący czwarty rodzic powoduje błąd.

$ 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>...]'

Jeśli chcesz wyodrębnić tylko rodziców, użyj ładnego formatu %P dla pełnych skrótów

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

lub %pdla skróconych rodziców.

$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb
Greg Bacon
źródło
wydaje się, że ^ poradzi sobie ze wszystkimi przypadkami i można się zastanawiać, dlaczego ~ pojawił się w pierwszej kolejności. Dlaczego nie pamiętasz tylko, jak ^ działa?
Sbu
to jest nadal bardzo mylące ... zakładając, że G jest GŁOWĄ, więc jeśli zrobię GŁOWĘ ^, to będzie D ... prawda?
Patoshi シ ト シ
12
@duckx wykres przebiega od góry do dołu, więc A jest ostatnim zatwierdzeniem, a G jest jednym z najstarszych. Ścieżka od G do D jest do przodu, a nie do tyłu, z tego co mogę powiedzieć.
goldenratio
@SimonBudin Myślę, że nie jest bardzo wygodny w użyciu ^^^^^^^, a nie ~7, prawda? Dlatego ~jest przydatny
JakowL,
1
@AdityaVikasDevarapalli To byłoby dobre jak własne pytanie.
Greg Bacon
340

Różnica między HEAD^i HEAD~jest dobrze opisana na ilustracji (autorstwa Jona Loeligera) znalezionej na stronie http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html .

Ta dokumentacja może być nieco niejasna dla początkujących, dlatego odtworzyłem tę ilustrację poniżej:

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
g_fred
źródło
12
Tylko jedno pytanie. Jak to możliwe, że zobowiązanie ma więcej niż dwoje rodziców? (Patrz B - rodzicami są D, E i F). Wyobrażam sobie, że jedynym sposobem, w jaki zatwierdzenie może mieć dwoje rodziców, jest to, gdy jest to zatwierdzenie scalania ... ale jak można scalić 3 zatwierdzenia w tym samym czasie?
tsikov
Jeśli się nie mylę, może to być oczywiste, ale myślę, że należy sprecyzować, że HEAD ~ podąża za bieżącą gałęzią (jak wspomniany poniżej Diego Dias).
fibono
2
Dodatkowo F = A^2^.
Mateen Ulhaq
2
Tak więc ^ == ^1 == LEFTMOST PARENT, ^2 == SECOND LEFTMOST PARENTi tak dalej. I ~ == ~1 == LEFTMOST PARENT, ~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT. W związku z tym~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
Alexander Torstling
1
@AlexanderTorstling to było dla mnie bardzo pomocne. Co jednak oznacza tutaj lewa i prawa strona?
polynomial_donut
287

Zarówno ~i ^same odnoszą się do rodzica zatwierdzenia ( ~~i ^^oba odnoszą się do zatwierdzenia dziadka, itp.) Ale różnią się znaczeniem, gdy są używane z liczbami:

  • ~2oznacza dwa poziomy w hierarchii , przez pierwszego rodzica, jeśli zatwierdzenie ma więcej niż jednego rodzica

  • ^2oznacza drugiego rodzica, w którym zatwierdzenie ma więcej niż jednego rodzica (tj. ponieważ jest to scalenie)

Można je łączyć, więc HEAD~2^3oznacza HEADto trzecie zatwierdzenie dziadka przez rodzica.

Matthew Strawbridge
źródło
2
Czytanie tego, a następnie zdjęcie z stackoverflow.com/questions/2221658/... miało doskonały sens.
kunigami,
23
To powinna być zaakceptowana odpowiedź, o wiele bardziej zwięzła i przydatna niż inne.
RichVel
3
Ta odpowiedź pozwoliła mi odróżnić karetkę / tyldę bez numeru i numeru! Myślałem, że ^^to samo, ^2ale tak nie jest.
Alexander Derck
278

Moje dwa centy...

wprowadź opis zdjęcia tutaj

Alex Janzik
źródło
A jak H=A~2^2nie H=A~2^1?
Mohammad Faisal,
3
Gdybym zorientowaliśmy się prawidłowo, rewizje A, B, D, Gsą na tej samej gałęzi i popełnić Djest scalanie z Ga Hzatem posiadanie dwóch rodziców. Zatem commit ( H) z innej gałęzi jest referencją ^2.
Mohammad Faisal,
62

Oto bardzo dobre wyjaśnienie wzięte dosłownie z http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :

ref~jest skrótem ref~1i oznacza pierwszego rodzica zatwierdzenia. ref~2oznacza pierwszego rodzica pierwszego zatwierdzenia. ref~3oznacza pierwszego rodzica pierwszego zatwierdzenia pierwszego rodzica pierwszego rodzica. I tak dalej.

ref^jest skrótem ref^1i oznacza pierwszego rodzica zatwierdzenia. Ale jeśli te dwie różnice się różnią, ref^2oznacza to, że drugi rodzic zatwierdzenia (pamiętaj, że commity mogą mieć dwoje rodziców, gdy są połączeniem).

Te ^i ~operatorzy mogą być łączone.

wprowadź opis zdjęcia tutaj

dr_
źródło
5
Dziękujemy za faktyczne wyjaśnienie różnic zamiast zamieszczania wielu przykładów.
Kirk Broadhurst
32

^<n>Format umożliwia wybranie ntą rodzica commit (istotne w scala). ~<n>Format pozwala wybrać n-tego przodka popełnienia, zawsze po pierwszym rodzicem. Zobacz kilka przykładów w dokumentacji git-rev-parse .

jamessan
źródło
21

Warto zauważyć, że git ma również składnię do śledzenia „skąd przyszedłeś” / „chcesz wrócić teraz” - na przykład HEAD@{1}będzie odwoływał się do miejsca, z którego przeskoczyłeś do nowej lokalizacji zatwierdzenia.

Zasadniczo HEAD@{}zmienne przechwytują historię ruchu HEAD i możesz zdecydować się na użycie konkretnej głowy, przeglądając dzienniki git za pomocą polecenia git reflog.

Przykład:

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

Przykładem może być to, że zrobiłem lokalne zatwierdzenia a-> b-> c-> d, a następnie wróciłem odrzucając 2 zatwierdzenia, aby sprawdzić mój kod - git reset HEAD~2- a potem chcę przenieść HEAD z powrotem do d - git reset HEAD@{1}.

Ashish
źródło
17

Prosto :

  • ~ określa przodków
  • ^ określa rodziców

Podczas łączenia możesz określić jeden lub więcej oddziałów. Następnie zatwierdzenie ma dwóch lub więcej rodziców, a następnie ^jest przydatne do wskazania rodziców.

Załóżmy, że jesteś na oddział A i masz dwa kolejne oddziały: B i C .

W każdym oddziale trzy ostatnie zatwierdzenia to:

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

Jeśli teraz na gałęzi A , wykonaj polecenie:

git merge B C

następnie łączysz ze sobą trzy gałęzie (tutaj twoje zatwierdzenie scalania ma troje rodziców)

i

~ wskazuje n-tego przodka w pierwszej gałęzi, więc

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

^ wskazuje n-tego rodzica, więc

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

Kolejne użycie ~lub ^obok siebie odbywa się w kontekście zatwierdzenia wyznaczonego przez poprzednie znaki.

Uwaga 1 :

  • HEAD~3jest zawsze równa: HEAD~~~i: HEAD^^^(każdy wskazuje A1 ),

        i ogólnie :

  • HEAD~njest zawsze równa: HEAD~...~( n razy ~) i: HEAD^...^( n razy ^).

Uwaga 2 :

  • HEAD^3nie jest tym samym co HEAD^^^(pierwszy wskazuje C3, a drugi A1 ),

        i ogólnie :

  • HEAD^1jest taka sama jak HEAD^,
  • ale dla n > 1: nieHEAD^n zawsze jest takie samo jak ( n razy ).HEAD^...^~
simhumileco
źródło
15

TLDR

~ jest tym, czego chcesz przez większość czasu, odwołuje przeszłe zatwierdzenia do bieżącej gałęzi

^ odniesienia do rodziców (git-merge tworzy drugiego lub więcej rodziców)

A ~ jest zawsze takie samo jak A ^
A ~~ jest zawsze takie samo jak A ^^, więc na
A ~ 2 nie jest to samo co A ^ 2,
ponieważ ~ 2 jest skrótem dla ~~,
podczas gdy ^ 2 nie jest skrót do czegokolwiek, oznacza drugiego rodzica

WeakPointer
źródło
11

HEAD ^^^ jest taki sam jak HEAD ~ 3, wybierając trzeci zatwierdzenie przed HEAD

HEAD ^ 2 określa drugą głowicę w zatwierdzeniu scalania

knittl
źródło
9
  • HEAD ~ określa pierwszego rodzica w „gałęzi”

  • HEAD ^ pozwala wybrać konkretnego rodzica zatwierdzenia

Przykład:

Jeśli chcesz podążać za boczną gałęzią, musisz określić coś takiego

master~209^2~15
Diego Dias
źródło
5

rzeczywisty przykład różnicy między HEAD ~ i HEAD ^

HEAD ^ VS HEAD ~

Anas Alpure
źródło
0

Mówiąc prosto, dla pierwszego poziomu pochodzenia (pochodzenie, dziedziczenie, rodowód itp.) HEAD ^ i HEAD ~ wskazują na to samo zatwierdzenie, które jest (zlokalizowane) jednego rodzica powyżej HEAD (zatwierdzenie).

Ponadto HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1. Ale HEAD ^^! = HEAD ^ 2! = HEAD ~ 2. Jeszcze HEAD ^^ = HEAD ~ 2. Czytaj.

Poza pierwszym poziomem pochodzenia, sprawy stają się trudniejsze, szczególnie jeśli działająca gałąź / gałąź główna uległa scaleniu (z innych gałęzi). Istnieje również kwestia składni z karetką, HEAD ^^ = HEAD ~ 2 (są równoważne) ALE HEAD ^^! = HEAD ^ 2 (to dwie różne rzeczy całkowicie).

Każda / karetka odnosi się do pierwszego rodzica HEAD, dlatego też kajdanki połączone razem są równoważne wyrażeniom tyldy, ponieważ odnoszą się do pierwszych rodziców pierwszego rodzica (pierwszego rodzica) itd., Itp., Ściśle w oparciu o liczbę na połączonych opiekach lub na liczbie następującej po znaku tyldy (w obu przypadkach oba oznaczają to samo), tj. zostań z pierwszym rodzicem i idź w górę x pokoleń.

HEAD ~ 2 (lub HEAD ^^) odnosi się do zatwierdzenia, które jest dwoma poziomami przodków powyżej / powyżej bieżącego zatwierdzenia (HEAD) w hierarchii, co oznacza zatwierdzenie dziadka HEAD.

Z kolei HEAD ^ 2 NIE odnosi się do zatwierdzenia drugiego rodzica pierwszego rodzica, ale po prostu do zatwierdzenia drugiego rodzica. Wynika to z tego, że znak karetki oznacza element nadrzędny zatwierdzenia, a liczba po nim wskazuje, do którego / do którego elementu nadrzędnego się odwołuje (pierwszy rodzic, w przypadku gdy po znaku karetki nie ma numeru [ponieważ jest on skrótem od liczby 1 oznacza pierwszego rodzica]). W przeciwieństwie do karetki liczba, która następuje po niej, nie oznacza kolejnego poziomu hierarchii w górę, ale raczej sugeruje, ile poziomów z boku, w hierarchii, należy znaleźć prawidłowego rodzica (zatwierdzenie). W przeciwieństwie do liczby w wyrażeniu tyldy, w hierarchii jest tylko jeden rodzic, niezależnie od liczby (natychmiast) kontynuującej daszek. Zamiast w górę, karetka ”

Więc HEAD ^ 3 jest równy trzeciemu rodzicowi zatwierdzenia HEAD (NIE pradziadkowi, co HEAD ^^^ I HEAD ~ 3 byłoby ...).

Sean Tank Garvey
źródło
-1

~ to znaczy rodzic.

^ jeśli ma rodziców dwóch lub więcej osób, takich jak scalanie zatwierdzenia, możemy wybrać drugą z rodzica lub inną.

więc jeśli tylko jedna rzecz (HEAD ~ lub HEAD ^), ma takie same wyniki.

Margaux
źródło