Git에서 HEAD ^와 HEAD ~의 차이점은 무엇입니까?


756

힘내에서 개체를 커밋 할 때 내가 조상을 지정, 나는 사이에 혼동하고있어 HEAD^HEAD~.

둘 다 같은 "번호"버전이 HEAD^3HEAD~2.

그들은 나에게 매우 유사하거나 똑같아 보이지만 물결표와 캐럿 사이에 차이점이 있습니까?


64
링크를 붙여 넣는 것은 나쁘지만 이것이 내가 찾은 가장 좋은 설명이며 그 안에 그림이 있습니다. paulboxley.com/blog/2011/06/git-caret-and-tilde
igor

4
링크가 끊어지면 특히 나쁩니다. 그래서 몇 가지 설명을 붙여 넣을 수 있기 때문에이를 방지하는 데 도움이되는 질문에 대답하는 것이 더 안전한 이유입니다.)
Samuel

답변:


763

엄지 손가락의 규칙

  • ~대부분의 시간을 사용 하여 여러 세대 (일반적으로 원하는 것)로 되돌아갑니다.
  • ^병합 커밋에 사용 -둘 이상의 (즉시) 부모가 있기 때문에

기억술:

  • 물결 ~모양은 거의 선형이며 직선으로 뒤로 가고 싶습니다.
  • 캐럿 ^은 도로에서 나무 나 포크의 흥미로운 부분을 제안합니다

틸데

의 "지정 개정"섹션 git rev-parse문서 를 정의 ~

<rev>~<n>master~3
접미사 ~<n>개정 파라미터는이 오브젝트 저지 수단 인 N 번째 지정된 발생 조상 만 시조 따라 오브젝트를 저지한다. 예를 들어, <rev>~3동등 <rev>^^^동등하다 <rev>^1^1^1...

뿐만 아니라 모든 커밋의 부모에게 갈 수 있습니다 HEAD. 예를 들어, master~2마스터 커밋 팁의 조부모를 의미하며 병합 커밋에서 첫 번째 부모를 선호합니다.

탈자 부호

Git 히스토리는 비선형 : DAG (Directed Acyclic Graph) 또는 트리입니다. A에 대한 하나의 부모 커밋 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문서는 개념적인 자식의 역사를 통해 많은 경로를 추적합니다. 시간은 일반적으로 아래쪽으로 흐릅니다. 커밋 D, F, B 및 A는 병합 커밋입니다.

다음은 Jon Loeliger의 그림입니다. 커밋 노드 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

아래 코드를 실행하여 인용 된 그림과 히스토리가있는 자식 저장소를 만듭니다.

#! /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 lolgit 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문서는 좋은 정보가 가득과 가치에 대한 깊이있는 읽기입니다. Pro Git 서적에서 Git Tools-Revision Selection 을 참조하십시오 .

학부모 커밋 순서

git 자신의 히스토리에서 커밋 89e4fcb0dd 는 머지 커밋으로, 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-parse89e4fcb0dd의 직계 부모를 순서대로 보여 달라고 요청하여 주문을 확인할 수 있습니다 .

$ 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가 HEAD라고 가정하면 HEAD ^를하면 D가 될 것입니다 ... 맞습니까?
Patoshi パ ト シ

12
@duckx 그래프는 실제로 위에서 아래로 가고 있으므로 A는 가장 최근의 커밋이고 G는 가장 오래된 커밋 중 하나입니다. 내가 말할 수있는 것에서 G에서 D로가는 길은 뒤로가 아니라 앞으로 나아갑니다.
goldenratio

@SimonBudin ^^^^^^^대신에 사용 하는 것이 매우 편리하지 ~7않습니까? 그것이 ~유용한 이유 입니다
YakovL

1
@AdityaVikasDevarapalli 그것은 그 자체의 질문으로 좋을 것입니다.
그렉 베이컨

340

차이 HEAD^HEAD~웰 (존 Loeliger 의해) 션에 의해 설명된다 발견 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 개의 커밋을 동시에 병합 할 수 있습니까?
tsikov

내가 실수하지 않으면 이것은 분명 할 수 있지만 HEAD ~가 현재 분기를 따르도록 지정해야한다고 생각합니다 (아래 언급 된 Diego Dias와 같이).
fibono

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
Alexander Torstling 1

1
@AlexanderTorstling 이것은 매우 도움이되었습니다. 그러나 여기서 왼쪽과 오른쪽은 무엇을 의미합니까?
polynomial_donut

287

모두 ~^자신에 커밋의 부모를 참조 ( ~~^^조부모 등, 커밋에 모두 참조) 그러나 그들이이 숫자와 함께 사용될 때 의미에서 차이 :

  • ~2커밋에 둘 이상의 부모가있는 경우 첫 번째 부모를 통해 계층 구조에서 두 수준을 의미 합니다.

  • ^2커밋에 둘 이상의 부모 가있는 두 번째 부모를 의미 합니다 (즉 병합이기 때문에)

그래서 이들은, 결합 될 수 HEAD~2^3수단 HEAD의 조부모의 세 번째 부모가 커밋 커밋.


2
이것을 읽고 stackoverflow.com/questions/2221658/… 에서 그림을 읽으면 완벽하게 이해되었습니다.
kunigami

23
이것은 다른 사람들보다 훨씬 간결하고 유용하게 받아 들여지는 대답이어야합니다.
RichVel

3
이 대답을 통해 숫자가없는 숫자와 캐럿이 없습니다. I의 생각 ^^과 동일 ^2하지만 아니다.
Alexander Derck

278

내 두 센트 ...

여기에 이미지 설명을 입력하십시오


그리고 어떻게 H=A~2^2하지 H=A~2^1?
Mohammad Faisal

3
내가 제대로 파악했다면, 커밋 A, B, D, G같은 지점에 있고 커밋 D(A)의 병합입니다 GH따라서 두 부모를 가진. 따라서 H다른 분기 의 커밋 ( ) 은로 참조됩니다 ^2.
Mohammad Faisal

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
많은 예제를 게시하는 대신 차이점을 실제로 설명해 주셔서 감사합니다.
Kirk Broadhurst 19

32

^<n>형식을 사용하면 커밋의 n 번째 부모 (병합과 관련됨)를 선택할 수 있습니다. 이 ~<n>형식을 사용하면 항상 첫 번째 상위를 따르는 n 번째 상위 커밋을 선택할 수 있습니다. 몇 가지 예는 git-rev-parse 설명서를 참조하십시오 .


21

git은 "from-where-you-come"/ "want-to-go-back-now"를 추적하기위한 구문을 가지고 있음을 주목할 가치가 있습니다. 예를 들어, 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다시 HEAD를 d-로 옮기고 싶습니다 git reset HEAD@{1}.


17

간단하게 :

  • ~ 조상을 지정
  • ^ 부모를 지정

병합 할 때 하나 이상의 분기를 지정할 수 있습니다. 그런 다음 커밋에는 둘 이상의 부모가 있으며 ^부모를 나타내는 데 유용합니다.

분기 A에 있고 BC 라는 두 개의 분기가 더 있다고 가정하십시오 .

각 브랜치에서 세 가지 마지막 커밋은 다음과 같습니다.

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

이제 지점 A에 있으면 다음 명령을 실행하십시오.

git merge B C

그런 다음 세 가지를 결합합니다 (여기서 병합 커밋에는 세 부모가 있습니다)

~ 첫 번째 분기에서 n 번째 조상을 나타냅니다.

  • HEAD~A3을 나타냅니다
  • HEAD~2A2를 나타냅니다
  • HEAD~3A1을 나타냅니다

^ n 번째 부모를 나타냅니다.

  • HEAD^A3을 나타냅니다
  • HEAD^2B3을 나타냅니다
  • HEAD^3C3을 나타냄

다음에 ~또는 ^서로 의 다음 사용은 이전 문자로 지정된 커밋의 맥락에서 이루어집니다.

고지 1 :

  • HEAD~3항상 같음 : HEAD~~~및 ~ : HEAD^^^(모두 A1 표시 ),

        그리고 일반적으로 :

  • HEAD~n: 항상 동일하다 HEAD~...~( N~)하고 : HEAD^...^( N^).

고지 2 :

  • HEAD^3이다 하지 같은 HEAD^^^((가) 제 나타내는 C3을 하고 두 번째 나타내는 A1을 )

        그리고 일반적으로 :

  • HEAD^1와 동일합니다 HEAD^.
  • 그러나 n > 1 :의 HEAD^n경우 항상 ( n 번 ) 과 동일 하지 않습니다 .HEAD^...^~

15

TLDR

~ 대부분의 시간을 원하는 것입니다. 현재 분기에 대한 과거 커밋을 참조합니다.

^ 부모를 참조합니다 (git-merge는 두 번째 부모 이상을 만듭니다).

A ~는 항상 A ^와 같습니다.
A ~~는 항상 A ^^와 같습니다. 따라서
A ~ 2는 A ^ 2와 같지 않습니다. 그러나
~ 2는 ~~의 축약 형
이지만 ^ 2는 아닙니다. 어떤 것에 대한 속기, 그것은 두 번째 부모를 의미


11

HEAD ^^^는 HEAD ~ 3과 동일하며 HEAD 전에 세 번째 커밋을 선택합니다.

HEAD ^ 2는 병합 커밋에서 두 번째 헤드를 지정합니다.


9
  • HEAD ~ "브랜치"의 첫 번째 부모를 지정합니다

  • HEAD ^를 사용하면 커밋의 특정 부모를 선택할 수 있습니다

예 :

사이드 브랜치를 따르려면 다음과 같이 지정해야합니다.

master~209^2~15


0

간단히 말해서, 첫 번째 레벨의 상위 (계급, 상속, 계보 등) HEAD ^ 및 HEAD ~에 대해 동일한 커밋을 가리키며, 이는 HEAD 위의 한 부모 (커밋)입니다.

또한 HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1입니다. 그러나 HEAD ^^! = HEAD ^ 2! = HEAD ~ 2. 그러나 HEAD ^^ = HEAD ~ 2. 읽어.

첫 번째 수준의 상위 항목을 넘어 서면, 특히 작업중인 지점 / 마스터 지점이 다른 지점에서 병합 한 경우 상황이 더 까다로워집니다. 캐럿과의 구문 문제도 있습니다. HEAD ^^ = HEAD ~ 2 (동등) BUT HEAD ^^! = HEAD ^ 2 (두 가지가 완전히 다릅니다).

각 캐럿은 HEAD의 첫 번째 부모를 나타내며 연결된 캐럿의 수를 엄격하게 기준으로 첫 번째 부모의 첫 번째 부모 등을 참조하기 때문에 함께 묶인 캐럿은 물결 표식과 같습니다. 또는 물결표 뒤의 숫자 (둘 다 같은 것을 의미 함), 즉 첫 번째 부모와 함께 있고 x 세대 위로 올라갑니다.

HEAD ~ 2 (또는 HEAD ^^)는 계층 구조에서 현재 커밋 (HEAD) 위 / 위의 두 가지 상위 수준 인 커밋을 나타내며, 이는 HEAD의 조부모 커밋을 의미합니다.

반면에 HEAD ^ 2는 첫 번째 부모의 두 번째 부모의 커밋이 아니라 단순히 두 번째 부모의 커밋을 나타냅니다. 캐럿은 커밋의 부모를 의미하기 때문에 다음 숫자는 부모 커밋이 참조되는 / 무엇을 의미 하는지를 나타냅니다 (캐럿 뒤에 숫자가없는 경우 첫 번째 부모는 [숫자가 짧기 때문에) 1, 첫 번째 부모를 의미]). 캐럿과 달리 이후에 나오는 숫자는 다른 수준의 계층 구조를 위쪽으로 나타내는 것이 아니라 계층 구조에 얼마나 많은 수준의 계층 구조가 있는지를 의미하며 올바른 부모를 찾기 위해 커밋해야합니다 (커밋). 물결 표현식의 숫자와 달리, 캐럿을 진행하는 숫자 (즉시)에 관계없이 계층 구조에서 상위에 하나만 있습니다. 위쪽이 아니라 캐럿

따라서 HEAD ^ 3은 HEAD 커밋의 세 번째 부모와 같습니다 (조부모님도 아닙니다. HEAD ^^^ AND HEAD ~ 3은 ...).


-1

~ 이것은 부모를 의미합니다.

^ 병합 커밋과 같이 둘 이상의 부모가있는 경우 부모 중 두 번째 또는 다른 것을 선택할 수 있습니다.

(HEAD ~ 또는 HEAD ^)와 같은 것이 하나라도 같은 결과를 가져 옵니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.