Git中HEAD ^和HEAD~之间有什么区别?

时间:2010-02-08 12:56:42

标签: git

当我在Git中指定一个祖先提交对象时,我在HEAD^HEAD~之间感到困惑。

两者都有“编号”版本,如HEAD^3HEAD~2

它们看起来与我非常相似或相同,但是波浪号和插入符号之间是否存在差异?

16 个答案:

答案 0 :(得分:568)

经验法则

  • 大多数情况下使用~ - 回归几代,通常是你想要的
  • 对合并提交使用^ - 因为他们有两个或更多(直接)父母

助记符:

  • Tilde ~外观几乎是线性的,并希望以直线向后移动
  • Caret ^表示路上一棵树或一个叉子的有趣部分

代字号

“Specifying Revisions” section of the git rev-parse documentation~定义为

  

<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等问题。 same section of the git rev-parse documentation将其定义为

  

<rev>^例如 HEAD^v1.5.1^0
  修订参数的后缀^表示该提交对象的第一个父级。 ^<n>表示 n 父级([例如] <rev>^等同于<rev>^1)。作为一项特殊规则,<rev>^0表示提交本身,并在<rev>是引用提交对象的标记对象的对象名时使用。

实施例

这些说明符或选择符可以任意链接,例如,英语中的topic~3^2是合并提交的第二个父级,即当前的祖父母(后三代)分支的提示topic

aforementioned section of the git rev-parse documentation通过名义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 and 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

“Specifying Revisions” in the git rev-parse documentation充满了很多信息,值得深入阅读。另请参阅书籍Git Tools - Revision Selection中的Pro Git

父母提交的命令

来自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-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>...]'

如果您只想提取父母,请使用pretty format %P获取完整哈希

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

%p缩写为父母。

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

答案 1 :(得分:321)

http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html上找到的插图(Jon Loeliger)很好地描述了HEAD^HEAD~之间的差异。

这个文档对初学者来说有点模糊,所以我再现了下面的插图:

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

答案 2 :(得分:259)

~^都指向提交的父级(~~^^都指祖父级提交等。)但它们不同在与数字一起使用时的含义:

  • ~2表示层次结构中的两个级别,如果提交具有多个父级,则通过第一个父级

  • ^2表示第二个父级,其中提交具有多个父级(即因为它是合并)

这些可以合并,因此HEAD~2^3表示HEAD祖父母提交的第三个父提交。

答案 3 :(得分:256)

我的两分钱......

enter image description here

答案 4 :(得分:45)

以下是http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde逐字记录的非常好的解释:

  

ref~ref~1的简写,表示提交的第一个父级。 ref~2表示提交的第一个父母的第一个父母。 ref~3表示提交的第一个父母的第一个父母的第一个父母。等等。

     

ref^ref^1的简写,表示提交的第一个父级。但是两者的不同之处在于ref^2表示提交的第二个父级(请记住,提交在合并时可以有两个父级)。

     

可以合并^~运算符。

enter image description here

答案 5 :(得分:31)

^<n>格式允许您选择提交的第n个父级(在合并中相关)。 ~<n>格式允许您选择第n个祖先提交,始终跟随第一个父级。有关示例,请参阅git-rev-parse的文档。

答案 6 :(得分:18)

值得注意的是,git还有一种语法可以跟踪“你来自哪里”/“现在想回去” - 例如,HEAD@{1}将引用该地点你跳到新提交位置的地方。

基本上HEAD@{}变量捕获HEAD移动的历史记录,您可以通过使用命令git reflog查看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-&gt; b-&gt; c-&gt; d然后我回去丢弃2次提交以检查我的代码 - git reset HEAD~2 - 之后我我希望将我的HEAD移回d - git reset HEAD@{1}

答案 7 :(得分:11)

<强>简单地

  • ~指定祖先
  • ^指定父母

合并时可以指定一个或多个分支。然后提交有两个或更多父母,然后^对于表示父母是有用的。

假设您在 A 分支上,并且还有两个分支: B C

在每个分支上,最后三个提交是:

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

如果现在在分支 A 上执行命令:

git merge B C

然后你将三个分支组合在一起(这里你的 merge 提交有三个父母)

~表示第一个分支中的第n个祖先,所以

  • HEAD~表示 A3
  • HEAD~2表示 A2
  • HEAD~3表示 A1

^表示第n个父母,所以

  • HEAD^表示 A3
  • HEAD^2表示 B3
  • HEAD^3表示 C3

~^的下一个使用是在前一个字符指定的提交的上下文中。

通知1

  • HEAD~3始终等于:HEAD~~~HEAD^^^(每个表示 A1 ),

,一般

  • HEAD~n始终等于:HEAD~...~ n ~)和:HEAD^...^ n ^)。

通知2

  • HEAD^3 HEAD^^^相同(第一个表示 C3 ,第二个表示 A1 ) ,

,一般

  • HEAD^1HEAD^
  • 相同
  • n &gt; 1:HEAD^n始终 HEAD^...^相同( n ~)。

答案 8 :(得分:10)

HEAD ^^^与HEAD~3相同,选择HEAD

之前的第三次提交

HEAD ^ 2指定合并提交中的第二个头

答案 9 :(得分:9)

TLDR

〜大多数时候都是你想要的,它引用过去提交到当前分支

^引用父项(git-merge创建第二个父项或更多)

A~始终与A ^相同
A〜总是与A ^^相同,依此类推
A~2与A ^ 2不同,但是,
因为~2是~~的简写
虽然^ 2不是任何简写,但它意味着第二个父亲

答案 10 :(得分:8)

  • HEAD~指定“分支”上的第一个父级

  • HEAD ^允许您选择提交的特定父级

一个例子:

如果您想要关注侧支,您必须指定类似

的内容
master~209^2~15

答案 11 :(得分:3)

HEAD〜和HEAD ^之间差异的实际示例

HEAD^ VS HEAD~

答案 12 :(得分:1)

^分支选择器
git checkout HEAD ^ 2
通过移至所选分支(在提交树上向后退一步)来选择(合并)提交的第二个分支

〜COMMIT选择器
git checkout HEAD〜2
在默认/选定分支上向后移动2次提交


到目前为止,我迄今为止在互联网上到处都看到的占主导地位的定义-包括PARENT选择器-定义了〜和^相对引用,包括官方的Git Book。是的,它们是PARENT选择器,但是这种“解释”的问题在于它完全违背了我们的目标:这就是如何区分这两个...:)

另一个问题是当我们鼓励使用^ BRANCH选择器进行COMMIT选择时(又名HEAD ^ === HEAD〜)。
同样,可以,您可以通过这种方式使用它,但这不是其设计目的。 ^ BRANCH选择器的向后移动行为是副作用,而不是其目的。

仅在合并提交时,可以将数字分配给^ BRANCH选择器。因此,它的全部容量只能在需要在分支之间进行选择的地方使用。 在分叉中表达选择内容的最直接方法是,踩到选定的路径/分支-在提交树上向后退一步。这只是副作用,而不是主要目的。

答案 13 :(得分:0)

~是父母。

^,如果它有两个或两个以上的父级,例如合并提交,我们可以选择第二个父级或另一个。

因此,如果只有一个东西((HEAD〜或HEAD ^)),它的结果就会相同

答案 14 :(得分:0)

如果您想在命令中键入HEAD^还是HEAD~,请只需使用其中一个

它们都是同一提交的名称-当前提交的第一父级。

master~master^相同-这两个名称都是master的第一个父级。

2 + 22 x 2都是4的方式相同-它们到达目的地的方式不同,但是答案是相同的。

这回答了一个问题:Git中的HEAD ^和HEAD〜有什么区别?

如果您只是进行了合并(因此您当前的提交中有多个父母),或者您仍然对插入符号和代字号的工作方式仍然感兴趣,请参阅其他答案(在此我将不重复)。深入的解释,以及如何重复使用它们(例如HEAD~~~)或使用数字(例如HEAD^2)。否则,我希望这个答案可以节省您一些时间。

答案 15 :(得分:-1)

简单地说,对于第一级父母(祖先,继承,血统等),HEAD ^和HEAD~都指向同一个提交,它位于HEAD(提交)之上的一个父级。

此外,HEAD ^ = HEAD ^ 1 = HEAD~ = HEAD~1。但是HEAD ^^!= HEAD ^ 2!= HEAD~2。但是HEAD ^^ = HEAD~2。请继续阅读。

除了第一级父母之外,事情变得更加棘手,特别是如果工作分支/主分支已经合并(来自其他分支)。插入符号的语法问题,HEAD ^^ = HEAD~2(它们相当)但是HEAD ^^!= HEAD ^ 2(它们完全是两个不同的东西)。

每个/插入符号指的是HEAD的第一个父母,这就是为什么插入音符相当于代字号表达式的原因,因为它们是指第一个父母的(第一个父母的)第一个父母等等,严格依赖于数字在连接的插入符号或波浪号后面的数字(无论哪种方式,它们都意味着相同的东西),即留在第一个父级并上升x代。

HEAD~2(或HEAD ^^)指的是在层次结构中当前提交(HEAD)上/上的两级祖先的提交,这意味着HEAD的祖父母提交。

另一方面,HEAD ^ 2不是指第一个父亲的第二个父母的提交,而是指第二个父母的提交。这是因为插入符号表示提交的父节点,后面的数字表示引用了哪个/哪个父提交(第一个父节点,在插入符号后面没有数字的情况下[因为它是数字的简写)为1,意思是第一个父母])。与插入符号不同,后面跟随的数字并不意味着另一层次的层次结构向上,而是意味着层次结构中有多少层次,需要找到正确的父级(提交)。与波形表达式中的数字不同,层次结构中只有一个父级,无论插入符号的数量(立即)如何。对于跨越等级的父母而言,插入符号的尾随数字不是向上的[在父母向上的水平上相当于连续插入的数量]。

所以HEAD ^ 3等于HEAD提交的第三个父级(不是曾祖父母,这就是HEAD ^^^ AND HEAD~3将是......)。