Answers:
~
的大部分时间-回去一数代的,通常你想要什么^
合并提交-因为它们有两个或更多(直接)父母助记符:
~
)的外观几乎是线性的,想直线后退^
建议道路上有趣的树或叉子部分本文档的“指定修订”部分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>
指Ñ 个父([ 例如 ]<rev>^
等同于<rev>^1
)。作为一条特殊规则,<rev>^0
是指提交本身,并且是<rev>
指引用提交对象的标记对象的对象名称时使用的。
这些说明符或选择器可以任意链接,例如,topic~3^2
英语是合并提交的第二个父级,它是分支当前尖端的曾祖父母(返回三代)topic
。
文档的上述部分git rev-parse
通过概念性的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
和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
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>...]'
如果您只想提取父母,请为完整的哈希使用漂亮的格式 %P
$ git log -1 --pretty=%P 89e4fcb0dd
c670b1f876521c9f7cd40184bf7ed05aad843433 649bf3a42f344e71b1b5a7f562576f911a1f7423 b67d40adbbaf4f5c4898001bf062a9fd67e43368
或%p
简略的父母。
$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb
^^^^^^^
代替不是很方便~7
,是吗?这就是为什么~
有用的原因
之间的差HEAD^
和HEAD~
深受图示(通过Jon Loeliger)描述上找到http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html。
本文档对于初学者可能有点晦涩,因此我复制了以下插图:
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
F = A^2^
。
^ == ^1 == LEFTMOST PARENT
,^2 == SECOND LEFTMOST PARENT
等等。而且~ == ~1 == LEFTMOST PARENT
,~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT
。通过扩展,~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
双方~
并^
就自己参考的提交父(~~
和^^
两个指祖父母提交等),但它们的时候都用数字用不同的意思:
~2
如果提交具有多个父级,则通过第一个父级在层次结构中表示两个级别
^2
表示提交中有多个父项的第二个父项(即,因为它是合并项)
这些可以组合,所以HEAD~2^3
意味着HEAD
'祖父母提交的第三父提交。
^^
相同,^2
但事实并非如此。
这是从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的引用日志来决定使用特定的头部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-commits a-> b-> c-> d,然后我放弃了2次提交以检查我的代码-- git reset HEAD~2
然后又想将HEAD移回d- git reset HEAD@{1}
。
简单地说:
~
指定祖先^
指定父母合并时可以指定一个或多个分支。然后一个提交有两个或多个父母,然后^
对指示父母很有用。
假设你是在分支一个和你有两个分支:乙和Ç。
在每个分支上,最后三个提交是:
如果现在在分支A上,则执行以下命令:
git merge B C
然后您将三个分支合并在一起(这里您的合并提交有三个父级)
和
~
表示第一个分支的第n个祖先
HEAD~
表示A3HEAD~2
表示A2HEAD~3
表示A1^
表示第n个父对象,因此
HEAD^
表示A3HEAD^2
表示B3HEAD^3
表示C3下一次使用的~
或^
在的上下文中彼此相邻的呈交由先前字符指定。
注意事项1:
HEAD~3
始终等于:HEAD~~~
和等于:(HEAD^^^
每个表示A1),通常:
HEAD~n
始终等于:HEAD~...~
(n次~
)和等于:HEAD^...^
(n次^
)。注意2:
HEAD^3
是不一样的HEAD^^^
(第一指示C3和第二指示A1),通常:
HEAD^1
与相同HEAD^
,HEAD^n
总是不相同HEAD^...^
(Ñ倍~
)。HEAD〜指定“分支”上的第一个父对象
HEAD ^允许您选择提交的特定父级
一个例子:
如果要遵循侧分支,则必须指定类似
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的第一父代,这就是为什么将在一起的插入号等同于代字号表达式的原因,因为它们严格地基于连接的插入符上的数字指代第一父代(第一父代)的第一父代等。或波浪号后面的数字(无论哪种方式,它们都表示同一件事),即与第一个父母住在一起并世世代代。
HEAD〜2(或HEAD ^^)指的是提交,它是层次结构中当前提交(HEAD)的上一级/上一级的提交,表示HEAD的祖父母提交。
另一方面,HEAD ^ 2不是指第一个父母的第二个父母的提交,而只是指第二个父母的提交。这是因为插入符号表示提交的父级,并且后面的数字表示引用哪个/哪个父提交(在插入符后没有数字的情况下,第一个父级[因为它是该数字的简写形式为1,表示第一个父级])。与插入符号不同,此后的数字并不表示层次结构向上另一级,而是表示在层次结构中侧身有多少个层次,需要找到正确的父级(提交)。与代字号表达式中的数字不同,该字符在层次结构中仅是一个父级,而不管(立即)插入号中的数字如何。插入符号不是向上,而是
因此HEAD ^ 3等于HEAD提交的第三个父对象(不是曾祖父母,这就是HEAD ^^^ AND HEAD〜3将是...)。