当我在Git中指定一个祖先提交对象时,我混淆了HEAD^和HEAD~。

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

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


当前回答

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

其他回答

简单地说:

~指定祖先 ^指定父母

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

假设你在分支A上,还有两个分支:B和C。

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

A: a1, a2, a3 B: b1 b2 b3 C: c1 c3 c3

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

git merge B C

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

and

~表示第一个分支中的第n个祖先,因此

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

^表示第n个父结点,因此

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

~或^的下一个用法是在前面的字符指定的提交上下文中。

注意1:

HEAD~3总是等于:HEAD~~~和:HEAD^^^(每个表示A1),

和一般:

HEAD~n总是等于:HEAD~…~ (n次~)和to: HEAD^…^ (n乘以^)

注意2:

HEAD^3和HEAD^^^不一样(第一个表示C3,第二个表示A1),

和一般:

HEAD^1和HEAD^是一样的, 但是对于n > 1: HEAD^n总是不等于HEAD^…^ (n乘以^)

以下是从http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde上逐字逐句摘录的一个很好的解释:

Ref ~是Ref ~1的简写,表示提交的第一个父对象。Ref ~2表示提交的第一个父对象的第一个父对象。Ref ~3表示提交的第一个父级的第一个父级的第一个父级。等等。 Ref ^是Ref ^1的简写,表示提交的第一个父对象。但两者的不同之处在于ref^2表示提交的第二个父对象(记住,当提交是一个merge时,它们可以有两个父对象)。 ^和~操作符可以组合使用。

我的意见…

^ BRANCH选择器 git结帐头^2 通过移动到所选分支(在提交树上后退一步),选择(合并)提交的第二个分支。

~提交选择器 git结帐头~2 在默认/选择的分支上向后提交2次


将~和^相对引用定义为PARENT选择器是迄今为止我在互联网上看到的主要定义——包括官方的Git Book。是的,它们是PARENT选择器,但这种“解释”的问题是,它完全违背了我们的目标:如何区分两者……:)

另一个问题是当我们被鼓励使用^ BRANCH选择器进行COMMIT选择时(也就是HEAD^ === HEAD~)。 同样,是的,你可以这样使用它,但这不是它的设计目的。^ BRANCH选择器的向后移动行为是一个副作用,而不是它的目的。

只有在合并提交时,才可以将数字分配给^ BRANCH选择器。因此,只有在需要在分支机构之间进行选择时,才能充分利用其容量。 在fork中表达选择的最直接的方法是踏上所选的路径/分支——这是在提交树上后退一步。这只是一种副作用,而不是它的主要目的。

值得注意的是,git还有一个用于跟踪“从哪里来的”/“想要返回-现在”的语法——例如,HEAD@{1}将引用您跳转到新提交位置的位置。

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

例子:

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

一个例子可以是我做了本地提交a->b->c->d,然后我回去丢弃2个提交来检查我的代码- git重置HEAD~2 -然后在那之后我想把我的HEAD移回d - git重置HEAD@{1}。