Яка різниця між HEAD ^ і HEAD ~ у Git?


756

Коли я вказую об’єкт вчинення предка в Git, я плутаюсь між HEAD^і HEAD~.

Обидва мають "пронумеровану" версію, як HEAD^3і HEAD~2.

Вони здаються мені дуже схожими чи однаковими, але чи є різниці між тильдом і каретою?


64
погано вставляти посилання, я знаю, але це найкраще пояснення, яке я знайшов, і в ньому є зображення. paulboxley.com/blog/2011/06/git-caret-and-tilde
igor

4
Посилання особливо погані, коли вони розірвані. Ось чому безпечніше відповісти на питання, які допомагають запобігти цьому через можливість копіювання вставити деякі пояснення :)
Самуїл

Відповіді:


763

Емпіричні правила

  • Використовуйте ~більшу частину часу - щоб повернутися на кілька поколінь, як правило, те, що вам потрібно
  • Використовуйте ^для об'єднань, оскільки вони мають двох або більше батьків

Мнемоніка:

  • Тільда ~майже лінійна на вигляд і хоче йти назад по прямій лінії
  • Карет ^пропонує цікавий відрізок дерева або вилки в дорозі

Тільда

Розділ "Вказівки на зміни" git rev-parseдокументації визначає ~як

<rev>~<n>, наприклад,master~3
суфікс ~<n>до параметра ревізії означає об'єкт фіксації, який є родоначальником n- го покоління названого об’єкта фіксації, слідуючи лише за першими батьками. Наприклад, <rev>~3еквівалент, <rev>^^^який еквівалентний <rev>^1^1^1...

Ви можете потрапити до батьків будь-яких зобов’язань, а не тільки HEAD. Ви також можете переміщатися назад через покоління: наприклад, master~2означає, що бабуся і дід з кінця головного відділення, надаючи перевагу першому батькові на об'єднанні.

Карет

Історія Git нелінійна: спрямований ациклічний графік (DAG) або дерево. Для вчинення лише одного з батьків, rev~і rev^означає те саме. Селектор карети стає корисним із об'єднанням, оскільки кожен є дитиною двох або більше батьків - і напружує мову, запозичену з біології.

HEAD^означає перший безпосередній батько кінця поточної гілки. HEAD^це короткий термін HEAD^1, і ви можете також звертатися HEAD^2до цього питання тощо. Цей же розділ git rev-parseдокументації визначає це як

<rev>^, наприклад HEAD^ ,v1.5.1^0
суфікс ^до параметра перегляду означає перший батьківський об'єкт цього об'єкта. ^<n>означає n- го батьків ([ наприклад ] <rev>^еквівалентно <rev>^1). Як спеціальне правило, <rev>^0означає саме фіксація і використовується, коли <rev>це ім'я об'єкта тега, який посилається на об'єкт фіксації.

Приклади

Ці специфікатори або селектори можуть бути ланцюгові довільно, наприклад , topic~3^2англійською мовою є другий батько комітету злиття, який є прадідом та прабатьком (три покоління назад) поточного кінця гілки topic.

У вищезгаданому розділі git rev-parseдокументації простежено багато шляхів через уявну історію git. Час тече загалом вниз. Коміти D, F, B і A - це об'єднання коммітів.

Ось ілюстрація Джона Лоліджера. Обидва вузли фіксації B і C є батьками вузла фіксації A. Батьківські коміти впорядковуються зліва направо.

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

Запустіть код нижче, щоб створити сховище git, історія якого відповідає наведеній ілюстрації.

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

Він додає псевдоніми в нове репо-викидання лише для, git lolіgit lola тому ви можете переглядати історію як у

$ 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

Зауважте, що на вашій машині назви об’єктів SHA-1 будуть відрізнятися від наведених вище, але теги дозволяють вам адресувати коміти за іменем та перевіряти своє розуміння.

$ 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

В «Завдання Ревізії» в git rev-parseдокументації сповнена корисної інформації і стоїть поглиблене читання. Див. Також Інструменти Git - Вибір редакції з книги Pro Git .

Порядок батьківських комітетів

Здійснити 89e4fcb0dd з власної історії GIT є злиття фіксації, так як git show 89e4fcb0ddвказує на те з рядку заголовка Merge , який відображає імена об'єктів Безпосереднім предків.

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 […]

Ми можемо підтвердити замовлення, попросивши git rev-parseпослідовно показати безпосередніх батьків 89e4fcb0dd.

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

Запит на неіснуючий четвертий батько призводить до помилки.

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

Якщо ви хочете витягнути лише батьків, використовуйте гарний формат %P для повних хешей

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

або %pдля скорочених батьків.

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

здається, ^ може впоратись із усіма справами, і можна дивуватися, чому ~ з'явився в першу чергу. Чому б не згадати лише те, як ^ працює?
Sbu

це все ще супер заплутано ... якщо припустити, що G - ГОЛОВА, тож якщо я зробить головку ^ це буде D ... правда?
Патоші パ ト シ

12
@duckx графік насправді йде зверху вниз, тому A - це остання фіксація, а G - одна з найдавніших. Шлях від G до D - це вперед, а не назад, з того, що я можу сказати.
goldenratio

@SimonBudin Я думаю, це не дуже зручно використовувати ^^^^^^^замість ~7, це? Ось чому ~корисно
YakovL

1
@AdityaVikasDevarapalli Це було б добре як власне питання.
Грег Бекон

340

Різниця між HEAD^і HEAD~добре описана ілюстрацією (Джоном Лоліджером), знайденою на http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html .

Ця документація може бути дещо незрозумілою для початківців, тому я відтворив цю ілюстрацію нижче:

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
Лише одне питання. Як можливо на зобов’язанні мати більше двох батьків? (Див. B - це батьки D, E і F) Я уявляю, що єдиний спосіб вчинення може мати два батьки - це коли це об'єкт злиття ... але як ви можете об'єднати 3 вчинки одночасно?
циков

Якщо я не помиляюся, це може бути очевидним, але я думаю, що слід вказати, що HEAD ~ слідує за поточною гілкою (на зразок Дієго Діаса, згаданого нижче).
фібоно

2
Крім того, F = A^2^.
Mateen Ulhaq

2
Таким чином, ^ == ^1 == LEFTMOST PARENT, ^2 == SECOND LEFTMOST PARENTі так далі. І ~ == ~1 == LEFTMOST PARENT, ~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT. За продовженням,~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
Олександр Торстлінг

1
@AlexanderTorstling це було дуже корисно для мене. Однак що тут означають лівий і правий?
polynomial_donut

287

І вони, ~і ^самостійно посилаються на батьківського складу комітету ( ~~і ^^обидва посилаються на вчинок бабусі і дідуся тощо), але вони різняться за значенням, коли їх використовують із числами:

  • ~2означає два рівні в ієрархії , через перший батьків, якщо комісія має більше одного з батьків

  • ^2означає другий з батьків, де у комітеті є більше одного з батьків (тобто тому, що це об'єднання)

Вони можуть поєднуватися, тобто HEAD~2^3означає HEAD, що третій з батьків вчиняє бабусь і дідусь.


2
Читання цього з подальшим малюнком із stackoverflow.com/questions/2221658/… мало ідеальний сенс.
kunigami

23
Це має бути прийнята відповідь, набагато більш лаконічна та корисна, ніж інші.
RichVel

3
Ця відповідь змусила мене розрізняти каре / тильду без номера та з номером! Я думав, що ^^це так само, ^2але це не так.
Олександр Дерк

278

Мої два центи ...

введіть тут опис зображення


А як H=A~2^2ні H=A~2^1?
Мохаммед Файсал

3
Якби я зрозумів це правильно, фіксацій A, B, D, Gзнаходяться на одній і тій же галузі і фіксації Dє злиття Gі H, отже , наявність двох батьків. Таким чином, посилання ( H) з іншої галузі є посиланням на ^2.
Мохаммед Фейсал

62

Ось дуже вдале пояснення, дослідне з http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :

ref~є скороченим ref~1і означає перший з батьків. ref~2означає перший з батьків перших батьків. ref~3означає перший батько першого батька комітету. І так далі.

ref^є скороченим ref^1і означає перший з батьків. Але там, де обидва відрізняються, це ref^2означає, що другий батько комітету (пам'ятайте, що коміти можуть мати двох батьків, коли вони є об'єднанням).

Оператори ^та ~оператори можуть бути об'єднані.

введіть тут опис зображення


5
Дякую, що насправді пояснили відмінності, а не розміщували безліч прикладів.
Кірк Бродхерст,

32

^<n>Формат дозволяє вибрати п - ю батька коммітов (відповідний в злиттях). ~<n>Формат дозволяє вибрати NTH предок фіксації, завжди після першого батька. Дивіться документацію git-rev-parse для деяких прикладів.


21

Варто зазначити, що git також має синтаксис для відстеження "звідки-куди-прийшов" / "хочу-повернутися назад" - наприклад, HEAD@{1}буде посилатися на місце, з якого ви перейшли до нового місця фіксації.

В основному HEAD@{}змінні фіксують історію руху HEAD, і ви можете вирішити використовувати певну голову, заглянувши у відривки git за допомогою команди git reflog.

Приклад:

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

Прикладом може бути те, що я зробив локальний-виконує a-> b-> c-> d, а потім повернувся, відкинувши 2 коміти, щоб перевірити свій код - git reset HEAD~2- а потім після цього я хочу перенести СЕГНУ назад до d - git reset HEAD@{1}.


17

Спрощено :

  • ~ уточнює предків
  • ^ уточнює батьків

Ви можете вказати одну або кілька гілок при злитті. Тоді в комітеті є два або більше батьків, а потім ^корисно вказати батьків.

Припустимо , що ви на гілки А і у вас є ще дві гілки: B і C .

У кожному відділенні три останні коміти:

  • A : A1 , A2 , A3
  • B : B1 , B2 , B3
  • С : С1 , С3 , С3

Якщо зараз на гілці A, ви виконуєте команду:

git merge B C

то ви поєднуєте три гілки разом (тут ваша команда злиття має трьох батьків)

і

~ вказує п’ятого предка в першій гілці, так

  • HEAD~вказує на A3
  • HEAD~2вказує на A2
  • HEAD~3позначає А1

^ позначає п’ятого батька, так

  • HEAD^вказує на A3
  • HEAD^2позначає B3
  • HEAD^3позначає С3

Наступне використання ~або ^поруч один з одним відбувається в контексті комітету, визначеного попередніми символами.

Повідомлення 1 :

  • HEAD~3завжди дорівнює: HEAD~~~і: HEAD^^^(кожне вказує на А1 ),

        і взагалі :

  • HEAD~nзавжди дорівнює: HEAD~...~( n разів ~) і: HEAD^...^( n разів ^).

Повідомлення 2 :

  • HEAD^3це НЕ те ж саме, HEAD^^^(перший вказує , С3 , а другий означає А1 ),

        і взагалі :

  • HEAD^1те саме, що HEAD^,
  • але для n > 1: HEAD^nзавжди не те саме, що HEAD^...^( n разів ~).

15

TLDR

~ це те, що ви хочете більшу частину часу, воно посилається на минулі фіксації до поточної гілки

^ посилання батьків (git-merge створює другий з батьків або більше)

A ~ завжди такий самий, як A ^
A ~~ завжди такий же, як A ^^, і так далі
A ~ 2 не є тим же, що A ^ 2, однак,
тому що ~ 2 є скороченням для ~~,
тоді як ^ 2 не є скорочення нічого, це означає 2-го з батьків


11

HEAD ^^^ - це те саме, що HEAD ~ 3, вибираючи третій комікс перед HEAD

HEAD ^ 2 вказує другу голову в об'єднанні


9
  • HEAD ~ вказує першого батька на "гілці"

  • HEAD ^ дозволяє вибрати конкретний батьківський запис

Приклад:

Якщо ви хочете слідувати бічною гілкою, вам слід вказати щось на кшталт

master~209^2~15


0

Простіше кажучи, для першого рівня батьківства (походження, успадкування, родовід тощо) HEAD ^ і HEAD ~ обидва вказують на один і той самий комітет, який знаходиться (розташований) одного з батьків над ГОЛОВОЮ (фіксація).

Крім того, HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1. Але ГОЛОВА ^^! = ГОЛА ^ 2! = ГОЛОВА ~ 2. І все-таки ГОЛОВА ^^ = ГОЛОВА ~ 2. Читайте далі.

Поза першим рівнем батьківства речі стають складнішими, особливо якщо робоча гілка / головна гілка мали злиття (з інших гілок). Є також питання синтаксису з каретою, HEAD ^^ = HEAD ~ 2 (вони еквівалентні) АЛЕ HEAD ^^! = HEAD ^ 2 (вони цілком дві різні речі).

Кожен / карета відноситься до першого батька HEAD, і тому догляд, зв'язаний разом, еквівалентний виразам тильди, тому що вони посилаються на перших батьків (перших батьків), перших батьків тощо, тощо. або за числом, що слідує за тильдою (в будь-якому випадку, вони обидва означають одне і те ж), тобто залишайтеся з першим батьком і продовжуйте x покоління.

HEAD ~ 2 (або HEAD ^^) посилається на коміт, який є двома рівнями походження вгору / над поточним комітетом (HEAD) в ієрархії, що означає вчинок бабусі і дідуся HEAD.

HEAD ^ 2, з іншого боку, посилається НЕ на прихильність другого батька першого батька, а просто на вчинення другого батька. Це тому, що карета означає батька комітету, а число, яке наступне, означає, на яке / яке батьківське зобов’язання йдеться (перший з батьків, у випадку, коли за каретою не слідує число [тому, що це скорочення для числа 1, значить перший батько]). На відміну від карети, число, яке випливає після цього, не означає іншого рівня ієрархії вгору, а, скоріше, означає, скільки рівнів збоку, в ієрархію, потрібно знайти правильний батьків (фіксація). На відміну від числа в виразі тильди, в ієрархії є лише один з батьків, незалежно від кількості (негайно) процедури, що переходить до карети. Замість вгору, карета '

Отже, HEAD ^ 3 дорівнює третьому батькові вчинення HEAD (НЕ прабабусі, це те, що HEAD ^^^ AND HEAD ~ 3 було б ...).


-1

~ це означає батьківський.

^ якщо у нього є батьки двох або більше, як об'єднати, ми можемо вибрати другого з батьків або іншого.

тому якщо лише одна річ, як (HEAD ~ або HEAD ^), вона має однакові результати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.