小编典典

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

all

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

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

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


阅读 216

收藏
2022-02-28

共1个答案

小编典典

经验法则

  • 使用~大部分时间——回溯几代,通常是你想要的
  • 用于^合并提交“因为它们有两个或更多(直接)父级

助记符:

  • 波浪~线在外观上几乎是线性的,并且想沿直线向后走
  • 插入符号^表示一棵有趣的树段或道路上的一个岔路口

波浪号

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

<rev>~<n>,例如master~3,修订参数
的后缀表示作为指定提交对象的第 n 代祖先的提交对象,仅在第一个父项之后。例如,等价于哪个等价于——~<n>
__<rev>~3``<rev>^^^``<rev>^1^1^1

您可以找到任何提交的父母,而不仅仅是HEAD. 您还可以通过世代向后移动:例如,master~2表示 master
分支尖端的祖父母,在合并提交时偏爱第一个父母。

插入符号

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

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

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

例子

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

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

这是 Jon Loeliger 的插图。提交节点 B 和 C 都是提交节点 A 的父节点。父提交按从左到右的顺序排列。(注意该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 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大量信息,值得深入阅读。另请参阅《 Pro
Git
》一书中的Git 工具 - 修订选择 __

父提交顺序

来自 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
2022-02-28