当我在Git中指定一个祖先提交对象时,我在HEAD^
和HEAD~
之间感到困惑。
两者都有像HEAD^3
和HEAD~2
这样的“编号”版本。
它们看起来与我非常相似或相同,但是波浪号和插入符之间有什么区别吗?
~
- 回归几代人,通常是你想要的^
- 因为它们有两个或更多(直接)父项口诀:
~
在外观上几乎是线性的,并且想要以直线向后移动^
建议在路上有一段有趣的树或叉子“Specifying Revisions” section of the git rev-parse
documentation将~
定义为
<rev>~<n>
,例如master~3
后缀~<n>
到revision参数表示提交对象,它是指定提交对象的第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
后缀^
到revision参数意味着该提交对象的第一个父级。^<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 <[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
查询不存在的第四个父级会导致错误。
$ 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
TLDR
〜是你大多数时候想要的,它引用过去提交到当前分支
^引用父级(git-merge创建第二个父级或更多)
A~总是与A ^相同 A〜总是与A ^^相同,依此类推 A~2与A ^ 2不同,但是, 因为~2是~~的简写 虽然^ 2不是任何简写,但它意味着第二个父亲
一个例子:
如果你想跟随一个侧支,你必须指定类似的东西
~
master~209^2~15
简单地说,对于第一级父母(祖先,继承,血统等),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将是...)。
HEAD^
和HEAD~
之间的区别由http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html上的插图(Jon Loeliger)很好地描述。
这个文档对初学者来说有点模糊,所以我再现了下面的插图:
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
表示层次结构中的两个级别,如果提交具有多个父级,则通过第一个父级^2
表示提交具有多个父项的第二个父项(即因为它是合并)这些可以组合,所以HEAD~2^3
意味着HEAD
的祖父母提交的第三个父提交。
我的两分钱......
这是http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde逐字逐句的非常好的解释:
ref~
是ref~1
的简写,意思是提交的第一个父母。ref~2
表示提交的第一个父母的第一个父母。ref~3
表示提交的第一个父母的第一个父母的第一个父母。等等。
ref^
是ref^1
的简写,意思是提交的第一个父母。但两者的不同之处在于ref^2
表示提交的第二个父级(请记住,提交在合并时可以有两个父级)。
^
和~
运营商可以合并。
^<n>
格式允许您选择提交的第n个父级(在合并中相关)。 ~<n>
格式允许您选择第n个祖先提交,始终跟随第一个父级。有关示例,请参阅git-rev-parse的文档。
值得注意的是,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
一个例子可能是我做了local-commit a-> b-> c-> d然后我回去丢弃2次提交来检查我的代码 - git reset HEAD~2
- 然后我想把我的HEAD移回d - git reset HEAD@{1}
。
简单地:
~
指定祖先^
指定父母您可以在合并时指定一个或多个分支。然后一个提交有两个或更多的父母,然后^
有助于表明父母。
假设你在分支A上,你还有两个分支:B和C.
在每个分支上,最后三个提交是:
如果现在在分支A上执行命令:
git merge B C
然后你将三个分支组合在一起(这里你的合并提交有三个父母)
和
~
表示第一个分支中的第n个祖先,所以
HEAD~
表示A3HEAD~2
表示A2HEAD~3
表示A1^
表示第n个父母,所以
HEAD^
表示A3HEAD^2
表示C3HEAD^3
或~
的下一个使用是在前一个字符指定的提交的上下文中。
通知1:
^
总是等于:HEAD~3
和:HEAD~~~
(每个表示A1),一般来说:
HEAD^^^
总是等于:HEAD~n
(n次HEAD~...~
)和:~
(n次HEAD^...^
)。通知2:
^
与HEAD^3
不同(第一个表示C3,第二个表示A1),一般来说:
HEAD^^^
与HEAD^1
相同,HEAD^
总是与HEAD^n
不同(n次HEAD^...^
)。HEAD ^^^与HEAD~3相同,选择HEAD之前的第三次提交
HEAD ^ 2指定合并提交中的第二个头