GitのHEAD ^とHEAD〜の違いは何ですか?


756

Gitで祖先コミットオブジェクトを指定すると、との間HEAD^で混乱しHEAD~ます。

HEAD^3とのような「番号付き」バージョンがありHEAD~2ます。

それらは私に非常に似ているか同じように見えますが、チルドとキャレットの間に違いはありますか?


64
リンクを貼り付けるのは悪いことですが、これは私が見つけた最良の説明であり、その中に画像があります。paulboxley.com/blog/2011/06/git-caret-and-tilde
イゴール

4
リンクが壊れていると、リンクは特に悪くなります。これが、いくつかの説明をコピーして貼り付けることができるので、これを防ぐのに役立つ質問に答える方が安全な理由です:)
Samuel

回答:


763

経験則

  • ~ほとんどの時間を使用します—何世代にもわたって戻ります。
  • ^マージコミットで使用— 2つ以上の(直接の)親があるため

ニーモニック:

  • ティルデ~は外観がほぼ線形で、直線で後戻りしたい
  • キャレット^は、道路のツリーまたはフォークの興味深いセグメントを示唆しています

チルダ

指定「の改訂」セクションgit rev-parseのドキュメントの定義~として

<rev>~<n>たとえばmaster~3、リビジョンパラメータの
サフィックスは、名前付きコミットオブジェクト~<n>n 番目の世代の祖先であるコミットオブジェクトを意味し、最初の親にのみ続きます。たとえば、「<rev>~3と同等」は「…」<rev>^^^と同等<rev>^1^1^1

だけでなく、任意のコミットの親に到達できますHEAD。世代間を移動することもできます。たとえば、master~2マスターブランチの先端の祖父母は、マージコミットで最初の親を優先します。

キャレット

Git履歴は非線形です:有向非循環グラフ(DAG)またはツリー。1つだけの親でコミットし、rev~そしてrev^同じことを意味します。キャレットセレクターはマージコミットで役立ちます。それぞれが2つ以上の親の子であり、生物学から借用された言語に負担をかけるからです。

HEAD^現在の枝の先端の最初の直接の親を意味します。HEAD^はの略でありHEAD^1HEAD^2必要に応じてアドレスなどを指定することもできます。ドキュメント同じセクションでは、次のようにgit rev-parse定義されています

<rev>^例えば HEAD^v1.5.1^0
接尾辞^リビジョンパラメータには、オブジェクトをコミットすることの最初の親を意味します。^<n>手段のn 番目の親([ ]は、<rev>^と等価です<rev>^1)。特別なルールとして、<rev>^0はコミット自体を意味し、がコミット<rev>オブジェクトを参照するタグオブジェクトのオブジェクト名である場合に使用されます。

これらの指定子またはセレクターは任意にチェーンできます。たとえばtopic~3^2英語では、ブランチの現在の先端のひ孫(3世代前)であるマージコミットの2番目の親ですtopic

ドキュメント前述のセクションではgit rev-parse、概念的なgit履歴を通じて多くのパスをたどっています。時間は一般的に下向きに流れます。コミット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

以下のコードを実行して、引用された図と履歴が一致する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のドキュメントは、偉大な情報がいっぱいですとANの深い読む価値があります。Gitツール-ブックPro Gitのリビジョン選択も参照してください。

親コミットの順序

直接の祖先のオブジェクト名を表示するMergeヘッダー行で示されているように、git自身の履歴からのコミット89e4fcb0ddはマージコミットgit show 89e4fcb0ddです。

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

存在しない4番目の親をクエリすると、エラーが発生します。

$ 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

^はすべてのケースを処理でき、なぜ〜が最初に現れたのか不思議に思うことができます。^のしくみだけを覚えてみませんか?
2017

これはまだ非常に混乱しています... GがHEADであると仮定すると、HEAD ^を実行すると、Dになります...正しいですか?
Patoshiパトシ

12
@duckxグラフは実際には上から下に向かっているため、Aは最新のコミット、Gは最も古いコミットの1つです。GからDへのパスは、私が言うことができるものから、前方ではなく後方です。
goldenratio

@SimonBudinおそらく、の^^^^^^^代わりに使用するのはあまり便利~7ではありませんね。だからこそ~便利です
YakovL 2018

1
@AdityaVikasDevarapalliそれはそれ自身の質問として良いでしょう。
グレッグベーコン

340

違いHEAD^HEAD~ http: //www.kernel.org/pub/software/scm/git/docs/git-rev-parse.htmlにあるイラスト(Jon Loeligerによる)でよく説明されています

このドキュメントは初心者には少しわかりにくいので、以下の図を再現しました。

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
一つだけ質問。コミットが3つ以上の親を持つことはどのように可能ですか?(Bを参照-親はD、E、Fです)コミットが2つの親を持つことができる唯一の方法は、それがマージコミットである場合だと思いますが、どうすれば3つのコミットを同時にマージできますか?
tsikov

私が間違えていなければ、これは明白かもしれませんが、HEAD〜が現在のブランチ(下記のDiego Diasのような)に従うことを指定する必要があると思います。
fibono 2015年

2
さらに、F = A^2^
Mateen Ulhaq 2016

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
@AlexanderTorstlingこれは私にとって非常に役に立ちました。しかし、ここで左と右はどういう意味ですか?
polynomial_donut

287

どちら~^自分では、コミット(の親を参照~~し、^^両方など、コミット祖父母を参照してください)しかし、彼らは彼らは番号を使用した場合の意味が異なります。

  • ~2コミットに複数の親がある場合、最初の親を介して、階層2レベル上に移動することを意味します

  • ^2コミットが複数のを持つ2番目の親を意味します(つまり、マージであるため)

これらは組み合わせることができるため、「祖父母のコミット」の3番目の親のコミットをHEAD~2^3意味しHEADます。


2
これに続いてstackoverflow.com/questions/2221658/…からの写真を読むと、完全に理解できました。
国頭村2015

23
これは受け入れられる答えであり、他のものよりもはるかに簡潔で有用なはずです。
RichVel 2016年

3
この回答により、番号のないキャレット/チルドと番号のあるキャレットを区別することができました!と^^同じだと思った^2けど違う。
Alexander Derck、2018年

278

私の2セント...

ここに画像の説明を入力してください


そして、どのようにH=A~2^2ではありませんかH=A~2^1
Mohammad Faisal

3
私はそれを正しく把握していた場合、コミットはABDG同じ枝上にあり、コミットDのマージであるGH、したがって、両親を持ちます。したがって、H他のブランチからのcommit()はによる参照^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、コミットの最初の親を意味します。しかし、2つが異なるのはref^2がコミットの2番目の親意味するということです(コミットの場合、コミットはマージ時に2つの親を持つことができます)。

演算子は組み合わせることができます。^~

ここに画像の説明を入力してください


5
多くの例を掲載するのではなく、実際に違いを説明していただきありがとうございます。
カークブロードハースト

32

この^<n>形式では、コミットのn番目の親を選択できます(マージに関連)。この~<n>形式では、常に最初の親に続くn番目の祖先コミットを選択できます。いくつかの例については、git-rev-parseのドキュメントを参照してください。


21

gitには "from-where-you-came" / "want-to-go-back-now"を追跡するための構文もあります。たとえば、HEAD@{1}新しいコミット場所にジャンプした場所から参照します。

基本的にHEAD@{}変数はHEADの動きの履歴をキャプチャし、コマンドを使用してgitのreflogsを調べることにより、特定の頭を使用することを決定できます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

単純に

  • ~ 祖先を指定します
  • ^ 親を指定します

マージ時に1つ以上のブランチを指定できます。次に、コミットには2つ以上の親があり、^親を示すのに役立ちます。

ブランチAにいて、さらに2つのブランチBCがあるとします。

各ブランチでの最後の3つのコミットは次のとおりです。

  • AA1A2A3
  • BB1B2B3
  • CC1C3C3

ここでブランチAで次のコマンドを実行します。

git merge B C

次に、3つのブランチを結合します(ここでは、マージコミットに3つの親あります)。

そして

~ 最初のブランチの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は2番目以上の親を作成します)

A ^としてA〜は常に同じである
A ~~は常にA ^^と同じであり、そのために
A〜2、しかし、A ^ 2と同じではありません
~~のために〜2は、簡略化したものですので、
しばらく^ 2ではありません何の略記でも、2番目の親を意味します


11

HEAD ^^^はHEAD〜3と同じで、HEADの前の3番目のコミットを選択します

HEAD ^ 2は、マージコミットの2番目のヘッドを指定します


9
  • HEAD〜は、「ブランチ」の最初の親を指定します

  • HEAD ^では、コミットの特定の親を選択できます

例:

サイドブランチをたどる場合は、次のように指定する必要があります

master~209^2~15


0

簡単に言うと、親子関係の最初のレベル(祖先、継承、系統など)の場合、HEAD ^とHEAD〜はどちらも同じコミットを指します。これは、HEAD(コミット)の1つの親に(配置されて)います。

さらに、HEAD ^ = HEAD ^ 1 = HEAD〜= HEAD〜1。しかし、HEAD ^^!= HEAD ^ 2!= HEAD〜2。しかし、HEAD ^^ = HEAD〜2。読む。

親子関係の最初のレベルを超えると、特に作業中のブランチ/マスターブランチに(他のブランチからの)マージがあった場合、事態はさらに複雑になります。キャレットの構文の問題もあります。HEAD^^ = HEAD〜2(これらは同等です)BUT HEAD ^^!= HEAD ^ 2(これらは完全に2つの異なるものです)。

それぞれ/キャレットはHEADの最初の親を参照します。接続されたキャレットの数に厳密に基づいて、最初の親(最初の親)の最初の親などを参照するため、一緒にストリング化されたキャレットがチルド式と同等であるのはこのためです。または、ティルダに続く数字(どちらの方法でも、どちらも同じ意味です)。つまり、最初の親にとどまり、x世代上に移動します。

HEAD〜2(またはHEAD ^^)は、階層内の現在のコミット(HEAD)の上位/上位の2つのレベルのコミットを指します。つまり、HEADの祖父母のコミットを意味します。

一方、HEAD ^ 2は、最初の親の2番目の親のコミットではなく、2番目の親のコミットを指します。これは、キャレットがコミットの親を意味し、次の数字は、どの/どの親コミットが参照されているかを示します(最初の親、キャレットの後に数字がない場合)[数字の省略形であるため1、最初の親を意味します])。キャレットとは異なり、その後に続く数字は、別のレベルの階層が上にあることを意味するのではなく、階層の横にいくつのレベルがあるかを意味し、正しい親(コミット)を見つける必要があります。チルダ式の数とは異なり、キャレットの数が(すぐに)進んでいるかどうかに関係なく、階層内で親は1つだけです。上向きではなく、キャレット

したがって、HEAD ^ 3は、HEADコミットの3番目の親と等しくなります(偉大な祖父母ではなく、HEAD ^^^ AND HEAD〜3は...)。


-1

~ これは親を意味します。

^ マージコミットのように、2つ以上の親がある場合は、2番目の親または別のものを選択できます。

したがって、(HEAD〜またはHEAD ^)のように1つでも同じ結果になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.