当我在 Git 中指定祖先提交对象时,我在 和 之间感到HEAD^困惑HEAD~。
HEAD^
HEAD~
两者都有一个“编号”版本,例如HEAD^3和HEAD~2。
HEAD^3
HEAD~2
它们对我来说似乎非常相似或相同,但是波浪号和插入符号之间有什么区别吗?
~
^
助记符:
文档的“指定修订”部分git rev-parse定义~为
git rev-parse
<rev>~<n>,例如master~3,修订参数 的后缀表示作为指定提交对象的第 n 代祖先的提交对象,仅在第一个父项之后。例如,等价于哪个等价于——~<n> __<rev>~3``<rev>^^^``<rev>^1^1^1
<rev>~<n>
master~3
~<n>
<rev>~3``<rev>^^^``<rev>^1^1^1
您可以找到任何提交的父母,而不仅仅是HEAD. 您还可以通过世代向后移动:例如,master~2表示 master 分支尖端的祖父母,在合并提交时偏爱第一个父母。
HEAD
master~2
Git 历史是非线性的:有向无环图 (DAG) 或树。对于只有一个父母的提交,rev~并且rev^意味着同样的事情。插入符号选择器对合并提交很有用,因为每个都是两个或多个父母的孩子 - 并且从生物学中借用语言。
rev~
rev^
HEAD^表示当前分支尖端的第一个 直接父级。HEAD^是 的缩写HEAD^1,也可以HEAD^2酌情寻址等。文档的同一部分将其git rev-parse定义为
HEAD^1
HEAD^2
<rev>^, 例如 HEAD^,v1.5.1^0 修订参数 的后缀^表示该提交对象的第一个父级。^<n>表示第 n 个父级([ eg ]<rev>^等价于<rev>^1)。作为特殊规则,<rev>^0表示提交本身,并且在<rev>引用提交对象的标记对象的对象名称时使用。
<rev>^
v1.5.1^0
^<n>
<rev>^1
<rev>^0
<rev>
这些说明符或选择器可以任意链接, 例如 ,topic~3^2在英语中是合并提交的第二个父级,它是当前分支尖端的曾祖父级(三代后)topic。
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
这是 Jon Loeliger 的插图。提交节点 B 和 C 都是提交节点 A 的父节点。父提交按从左到右的顺序排列。(注意该git log --graph命令以相反的顺序显示历史记录。)
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
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显示直接祖先对象名称的合并标题行所示。
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