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ą?
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ą?
Odpowiedzi:
~większość czasu - aby cofnąć się o kilka pokoleń, zwykle tego, czego chcesz^przy łączeniu zatwierdzeń - ponieważ mają dwóch lub więcej (bezpośrednich) rodzicówMnemonika:
~ma prawie liniowy wygląd i chce cofać się w linii prostej^sugeruje interesujący fragment drzewa lub widelca na drodzeW 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.
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ładHEAD^,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.
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 .
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 <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 […]
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
^^^^^^^, a nie ~7, prawda? Dlatego ~jest przydatny
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
F = A^2^.
^ == ^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
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.
^^to samo, ^2ale tak nie jest.
Moje dwa centy...

H=A~2^2nie H=A~2^1?
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.
Oto bardzo dobre wyjaśnienie wzięte dosłownie z http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :
ref~jest skrótemref~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ótemref^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.
^<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 .
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}.
Prosto :
~ określa przodków^ określa rodzicówPodczas łą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:
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 A3HEAD~2wskazuje A2HEAD~3wskazuje A1^ wskazuje n-tego rodzica, więc
HEAD^wskazuje A3HEAD^2wskazuje B3HEAD^3wskazuje C3Kolejne 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^,HEAD^n zawsze jest takie samo jak ( n razy ).HEAD^...^~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
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
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 ...).