1. ホーム
  2. git

[解決済み] GitのHEAD^とHEAD~の違いは何ですか?

2022-03-17 21:18:01

質問

Git で祖先のコミット・オブジェクトを指定する際、以下のように混乱します。 HEAD^HEAD~ .

どちらも、以下のような番号付きバージョンです。 HEAD^3HEAD~2 .

私にはとても似ているというか、同じに見えるのですが、チルダとキャレットの違いはあるのでしょうか?

どのように解決するのですか?

経験則

  • 使用方法 ~ ほとんどの場合、何世代もさかのぼることができます。
  • 使用する ^ マージコミットの場合 - 2つ以上の (直接の) 親を持つため

ニーモニック

  • ティルデ ~ は、見た目はほぼ直線的で、まっすぐ後方に進みたがる
  • キャレット ^ 分岐点

ティルデ

"リビジョンの指定 "セクションの git rev-parse ドキュメント が定義する ~ として

<rev>~<n> , 例. master~3
接尾語 ~<n> をリビジョンパラメータに指定すると、そのコミットオブジェクトが n th 指定されたコミットオブジェクトの世代の先祖で、最初の親にのみ続く。例えば <rev>~3 は、次のものと同等です。 <rev>^^^ と等価である。 <rev>^1^1^1 ...

に限らず、あらゆるコミットの親を取得することができます。 HEAD . また、世代を遡ることもできます:例えば master~2 は master ブランチの先端の祖父母を意味し、マージコミットでは最初の親が優先されます。

キャレット

Git の歴史は非線形です。有向無サイクル・グラフ (DAG) あるいはツリーです。親が一人しかいないコミットの場合 rev~rev^ は同じものを意味します。キャレットセレクターはマージコミットにおいて有用です。なぜなら、マージコミットはそれぞれが2つ以上の親の子であり、生物学から借用した言語を用いているからです。

HEAD^ は、最初の 即時 現在のブランチの先端の親 HEAD^HEAD^1 というアドレスも可能です。 HEAD^2 などを適宜選択してください。その と同じセクションを git rev-parse ドキュメント は、次のように定義しています。

<rev>^ , など HEAD^ , v1.5.1^0
接尾語 ^ のリビジョンパラメーターは、そのコミットオブジェクトの最初の親を意味します。 ^<n> n th 親([. ] <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の親です。親コミットは左から右の順に並んでいます。(N.B. git log --graph コマンドは履歴を逆の順番で表示します)。

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 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 ドキュメンテーション は素晴らしい情報が満載で、深く読み込む価値があります。以下もご参照ください。 Git ツール - リビジョン選択 書籍より プロGit .

親コミットの順番

コミット 89e4fcb0dd はマージコミットです。 git show 89e4fcb0dd は、直前の先祖のオブジェクト名を表示する Merge ヘッダー行で示されます。

<ブロッククオート
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 […]

と聞けば、注文を確認することができます。 git rev-parse で、89e4fcb0dd の直系の親を順番に表示します。

$ 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