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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2221658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 07:48:43  来源:igfitidea点击:

What's the difference between HEAD^ and HEAD~ in Git?

git

提问by TK.

When I specify an ancestor commit object in Git, I'm confused between HEAD^and HEAD~.

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

Both have a "numbered" version like HEAD^3and HEAD~2.

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

They seem very similar or the same to me, but are there any differences between the tilde and the caret?

它们在我看来非常相似或相同,但是波浪号和插入符号之间有什么区别吗?

回答by Greg Bacon

Rules of thumb

经验法则

  • Use ~most of the time — to go back a number of generations, usually what you want
  • Use ^on merge commits — because they have two or more (immediate) parents
  • 使用~的大部分时间-回去一数代的,通常你想要什么
  • 用于^合并提交——因为它们有两个或多个(直接)父项

Mnemonics:

助记符:

  • Tilde ~is almost linear in appearance and wants to go backward in a straight line
  • Caret ^suggests an interesting segment of a tree or a fork in the road
  • 波浪号~在外观上几乎是线性的,想要直线倒退
  • 插入符号^表示树的有趣部分或道路上的岔路口

Tilde

波浪号

The “Specifying Revisions” section of the git rev-parsedocumentationdefines ~as

文档“指定修订”部分git rev-parse定义~

<rev>~<n>, e.g. master~3
A suffix ~<n>to a revision parameter means the commit object that is the nthgeneration ancestor of the named commit object, following only the first parents. For example, <rev>~3is equivalent to <rev>^^^which is equivalent to <rev>^1^1^1

<rev>~<n>,例如master~3,修订参数的
后缀~<n>表示提交对象是命名提交对象的n代祖先,仅在第一个父代之后。例如,相当于哪个相当于……<rev>~3<rev>^^^<rev>^1^1^1

You can get to parents of any commit, not just HEAD. You can also move back through generations: for example, master~2means the grandparent of the tip of the master branch, favoring the first parent on merge commits.

您可以联系任何提交的父母,而不仅仅是HEAD. 您还可以通过几代向后移动:例如,master~2表示 master 分支尖端的祖父级,在合并提交时优先考虑第一个父级。

Caret

插入符号

Git history is nonlinear: a directed acyclic graph (DAG) or tree. For a commit with only one parent, rev~and rev^mean the same thing. The caret selector becomes useful with merge commits because each one is the child of two or more parents — and strains language borrowed from biology.

Git 历史是非线性的:有向无环图 (DAG) 或树。对于一个犯了只有一个父,rev~rev^意味着同样的事情。插入符号选择器在合并提交中变得很有用,因为每个提交都是两个或多个父母的孩子——并且紧张从生物学借来的语言。

HEAD^means the first immediateparent of the tip of the current branch. HEAD^is short for HEAD^1, and you can also address HEAD^2and so on as appropriate. The same section of the git rev-parsedocumentationdefines it as

HEAD^表示当前分支尖端的第一个直接父级。HEAD^是 的缩写HEAD^1,您也可以根据需要进行寻址HEAD^2等。文档同一部分将其git rev-parse定义为

<rev>^, e.g.HEAD^, v1.5.1^0
A suffix ^to a revision parameter means the first parent of that commit object. ^<n>means the nthparent ([e.g.] <rev>^is equivalent to <rev>^1). As a special rule, <rev>^0means the commit itself and is used when <rev>is the object name of a tag object that refers to a commit object.

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

Examples

例子

These specifiers or selectors can be chained arbitrarily, e.g., topic~3^2in English is the second parent of the merge commit that is the great-grandparent (three generations back) of the current tip of the branch topic.

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

The aforementioned section of the git rev-parsedocumentationtraces many paths through a notional git history. Time flows generally downward. Commits D, F, B, and A are merge commits.

文档上述部分git rev-parse通过名义上的 git 历史跟踪了许多路径。时间通常向下流动。提交 D、F、B 和 A 是合并提交。

Here is an illustration, by Jon Loeliger. Both commit nodes B and C are parents of commit node A. Parent commits are ordered left-to-right.

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

这是 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

Run the code below to create a git repository whose history matches the quoted illustration.

运行下面的代码以创建一个 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 "
$ 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
: git commit-tree failed" if $?; system("git tag -a -m '$sha1{$root}' '$root' '$sha1{$root}'") == 0 or die "
$ 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 tag failed"; }
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 […]
=~ s!^.*/!!; # / fix Stack Overflow highlighting my $repo = mkdtemp "repoXXXXXXXX"; chdir $repo or die "
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 […]
: chdir: $!"; system("git init") == 0 or die "
$ git rev-parse 89e4fcb0dd^1 89e4fcb0dd^2 89e4fcb0dd^3
c670b1f876521c9f7cd40184bf7ed05aad843433
649bf3a42f344e71b1b5a7f562576f911a1f7423
b67d40adbbaf4f5c4898001bf062a9fd67e43368
: git init failed"; chomp(my $tree = `git write-tree`); die "
$ 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>...]'
: git write-tree failed" if $?; postorder 'A', $tree; system "git update-ref HEAD $sha1{A}"; die "
$ git log -1 --pretty=%P 89e4fcb0dd
c670b1f876521c9f7cd40184bf7ed05aad843433 649bf3a42f344e71b1b5a7f562576f911a1f7423 b67d40adbbaf4f5c4898001bf062a9fd67e43368
: git update-ref failed" if $?; system "git update-ref master $sha1{A}"; die "
$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb
: 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'";

It adds aliases in the new throwaway repo only for git loland git lolaso you can view history as in

它增加了在新的一次性回购别名只为git lolgit lola这样你就可以查看历史记录,如

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

Note that on your machine the SHA-1 object names will differ from those above, but the tags allow you to address commits by name and check your understanding.

请注意,在您的机器上,SHA-1 对象名称将与上述名称不同,但标签允许您按名称处理提交并检查您的理解。

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

The “Specifying Revisions” in the git rev-parsedocumentationis full of great information and is worth an in-depth read. See also Git Tools - Revision Selectionfrom the book Pro Git.

文档中“Specifying Revisions”git rev-parse包含大量信息,值得深入阅读。另请参阅Pro Git一书中的Git 工具 - 修订选择

Order of Parent Commits

父提交顺序

The commit 89e4fcb0ddfrom git's own history is a merge commit, as git show 89e4fcb0ddindicates with the Merge header line that displays the immediate ancestors' object names.

来自 git 自己历史的提交89e4fcb0dd是一个合并提交,正如git show 89e4fcb0dd显示直接祖先对象名称的 Merge 标题行所示。

git merge B C
##代码##

We can confirm the ordering by asking git rev-parseto show 89e4fcb0dd's immediate parents in sequence.

我们可以通过要求按顺序git rev-parse显示 89e4fcb0dd 的直接父母来确认排序。

##代码##

Querying the non-existent fourth parent results in an error.

查询不存在的第四个父项会导致错误。

##代码##

If you want to extract the parents only, use pretty format%Pfor the full hashes

如果您只想提取父母,请对完整哈希使用漂亮的格式%P

##代码##

or %pfor abbreviated parents.

%p缩写父母。

##代码##

回答by g_fred

The difference between HEAD^and HEAD~is well described by the illustration (by Jon Loeliger) found on http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html.

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

This documentation can be a bit obscure to beginners so I've reproduced that illustration below:

这个文档对初学者来说可能有点晦涩,所以我复制了下面的插图:

##代码##

回答by Matthew Strawbridge

Both ~and ^on their own refer to the parent of the commit (~~and ^^both refer to the grandparent commit, etc.) But they differ in meaning when they are used with numbers:

双方~^就自己参考的提交父(~~^^两个指祖父母提交等),但它们的时候都用数字用不同的意思:

  • ~2means up two levels in the hierarchy, via the first parent if a commit has more than one parent

  • ^2means the second parentwhere a commit has more than one parent (i.e. because it's a merge)

  • ~2意味着在层次结构中向上两级,如果提交有多个父级,则通过第一个父级

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

These can be combined, so HEAD~2^3means HEAD's grandparent commit's third parent commit.

这些可以组合,因此HEAD~2^3意味着HEAD的祖父母提交的第三个父提交。

回答by Alex Janzik

My two cents...

我的两分钱...

enter image description here

在此处输入图片说明

回答by dr_

Here's a very good explanation taken verbatim from http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde:

这是一个很好的解释,逐字逐句来自http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde

ref~is shorthand for ref~1and means the commit's first parent. ref~2means the commit's first parent's first parent. ref~3means the commit's first parent's first parent's first parent. And so on.

ref^is shorthand for ref^1and means the commit's first parent. But where the two differ is that ref^2means the commit's second parent (remember, commits can have two parents when they are a merge).

The ^and ~operators can be combined.

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

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

^~运营商可以进行组合。

enter image description here

在此处输入图片说明

回答by jamessan

The ^<n>format allows you to select the nth parent of the commit (relevant in merges). The ~<n>format allows you to select the nth ancestor commit, always following the first parent. See git-rev-parse's documentation for some examples.

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

回答by ashish

It is worth noting that git also has a syntax for tracking "from-where-you-came"/"want-to-go-back-now" - for example, HEAD@{1}will reference the place from where you jumped to new commit location.

值得注意的是,git 也有一个语法来跟踪“from-where-you-come”/“want-to-go-back-now”——例如,HEAD@{1}将引用你跳转到新提交位置的位置。

Basically HEAD@{}variables capture the history of HEAD movement, and you can decide to use a particular head by looking into reflogs of git using the command git reflog.

基本上,HEAD@{}变量捕获 HEAD 移动的历史,您可以通过使用命令查看 git 的 reflogs 来决定使用特定的 head git reflog

Example:

例子:

##代码##

An example could be that I did local-commits a->b->c->d and then I went back discarding 2 commits to check my code - git reset HEAD~2- and then after that I want to move my HEAD back to d - git reset HEAD@{1}.

一个例子可能是我做了本地提交 a->b->c->d 然后我回去丢弃 2 个提交来检查我的代码 -git reset HEAD~2然后我想将我的 HEAD 移回 d - git reset HEAD@{1}

回答by simhumileco

Simplistically:

简单地说

  • ~specifies ancestors
  • ^specifies parents
  • ~指定祖先
  • ^指定父母

You can specify one or more branches when merging. Then a commit has two or more parents and then ^is useful to indicate parents.

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

Suppose you are on branch Aand you have two more branches: Band C.

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

On each branch the three last commits are:

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

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

If now on branch Ayou execute the command:

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

##代码##

then you are combining three branches together (here your mergecommit has three parents)

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

and

~indicates the n'th ancestor in the first branch, so

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

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

^indicates the n'th parent, so

^表示第 n 个父节点,所以

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

The next use of ~or ^next to each other is in the context of the commit designated by previous characters.

下一次使用~^彼此相邻是在由先前字符指定的提交的上下文中。

Notice 1:

注意事项1

  • HEAD~3is always equal to: HEAD~~~and to: HEAD^^^(every indicates A1),
  • HEAD~3始终等于:HEAD~~~和:(HEAD^^^每个都表示A1),

        and generally:

        通常

  • HEAD~nis always equal to: HEAD~...~(ntimes ~) and to: HEAD^...^(ntimes ^).
  • HEAD~n总是等于:HEAD~...~n~)和:HEAD^...^n^)。

Notice 2:

注意事项2

  • HEAD^3is notthe same as HEAD^^^(the first indicates C3and the second indicates A1),
  • HEAD^3一样的HEAD^^^(第一指示C3和第二指示A1),

        and generally:

        通常

  • HEAD^1is the same as HEAD^,
  • but for n> 1: HEAD^nis always notthe same as HEAD^...^(ntimes ~).
  • HEAD^1是一样的HEAD^
  • 但对于Ñ> 1:HEAD^n总是相同HEAD^...^Ñ~)。

回答by WeakPointer

TLDR

TLDR

~ is what you want most of the time, it references past commits to the current branch

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

^ references parents (git-merge creates a 2nd parent or more)

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

A~ is always the same as A^
A~~ is always the same as A^^, and so on
A~2 is not the same as A^2 however,
because ~2 is shorthand for ~~
while ^2 is not shorthand for anything, it means the 2nd parent

A~ 总是与 A^ 相同
A~~ 总是与 A^^ 相同,等等
A~2 与 A^2 不同,
因为 ~2 是 ~~ 的简写
而 ^2 不是任何东西的简写,这意味着第二个父母

回答by knittl

HEAD^^^ is the same as HEAD~3, selecting the third commit before HEAD

HEAD^^^ 和HEAD~3一样,选择HEAD之前的第三个commit

HEAD^2 specifies the second head in a merge commit

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