Какая разница между HEAD ^ и HEAD ~ в Git?
Когда я укажу объект фиксации предка в Git, я запутался между HEAD^
и HEAD~
.
Оба имеют "нумерованную" версию, например HEAD^3
и HEAD~2
.
Они кажутся очень похожими или одинаковыми для меня, но существуют ли различия между тильдой и кареткой?
Ответы
Ответ 1
Правила большого пальца
- Большую часть времени пользуйтесь
~
- чтобы вернуться на несколько поколений назад, обычно то, что вы хотите
- Используйте
^
для коммитов слияния - потому что у них два или более (непосредственных) родителя
Мнемонические:
- Тильда
~
выглядит почти линейно и хочет идти назад по прямой линии
- Карет
^
предлагает интересный отрезок дерева или развилку на дороге
Тильда
В разделе "Указание редакций" документации git rev-parse
определяется ~
как
<rev>~<n>
, например master~3
Суффикс ~<n>
к параметру ревизии означает объект фиксации, который является прародителем n-го поколения именованного объекта фиксации, следуя только первым родителям. [Например,] <rev>~3
эквивалентен <rev>^^^
, что эквивалентно <rev>^1^1^1
& hellip;
Вы можете получить к родителям любой коммит, а не только HEAD
. Вы также можете перемещаться назад через поколения: например, master~2
означает прародителя кончика главной ветки, отдавая предпочтение первому родителю при фиксации слияния.
Caret
История Git нелинейная: ориентированный ациклический граф (DAG) или дерево. Для коммита только с одним родителем rev~
и rev^
означают одно и то же. Селектор каретки становится полезным с коммитами слияния, потому что каждый из них является потомком двух или более родителей - и напрягает язык, заимствованный из биологии.
HEAD^
означает первого непосредственного родителя кончика текущей ветки. HEAD^
является сокращением от HEAD^1
, и вы также можете обратиться к HEAD^2
и так далее, в зависимости от ситуации. тот же раздел документации git rev-parse
определяет его как
<rev>^
, например HEAD^
, v1.5.1^0
Суффикс ^
к параметру ревизии означает первого родителя этого объекта фиксации. ^<n>
означает n-го родителя ([например, <rev>^
эквивалентен <rev>^1
). Как специальное правило, <rev>^0
означает сам коммит и используется, когда <rev>
является именем объекта тега, который ссылается на объект фиксации.
Примеры
Эти спецификаторы или селекторы могут быть произвольно соединены в цепочку, например, topic~3^2
на английском языке является вторым родителем коммита слияния, который является пра-прародителем (три поколения назад) текущего наконечника ветки topic
.
В вышеупомянутом разделе документации git rev-parse
прослеживается множество путей в истории условных мерзавцев. Время течет в основном вниз. Коммиты D, F, B и A являются коммитами слияния.
Вот иллюстрация Джона Лелигера. Оба узла фиксации 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 Qaru 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
содержит много полезной информации и заслуживает подробного прочтения. См. также Инструменты Git - выбор редакции из книги Pro Git.
Порядок родительских комиссий
Коммит 89e4fcb0dd из собственной истории gits является коммитом слияния, как указывает git show 89e4fcb0dd
в строке заголовка слияния, которая отображает имена объектов непосредственных предков.
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
показать 89e4fcb0dds ближайших родителей в последовательности.
$ 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
Ответ 2
Разница между HEAD^
и HEAD~
хорошо описана иллюстрацией (Джон Лоулигер), найденной на 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
Ответ 3
Оба ~
и ^
сами по себе относятся к родительскому элементу commit (~~
и ^^
оба относятся к фиксации grandparent и т.д.). Но они различаются по смыслу, когда они используются с числами
-
~2
означает до двух уровней в иерархии, через первого родителя, если commit имеет более одного родителя
-
^2
означает второй родительский, где commit имеет более одного родителя (т.е. из-за его слияния)
Они могут быть объединены, поэтому HEAD~2^3
означает HEAD
grandparent commit третьего родительского фиксации.
Ответ 4
Мои два цента...
Ответ 5
Здесь очень хорошее объяснение, взятое дословно из http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde:
ref~
является сокращением для ref~1
и означает фиксацию первого родителя. ref~2
означает первый родительский родительский элемент. ref~3
означает первый первый родительский родительский родительский элемент. И так далее.
ref^
является сокращением для ref^1
и означает фиксацию первого родителя. Но где два отличаются друг от друга тем, что ref^2
означает фиксацию второго родителя (помните, что коммиты могут иметь двух родителей, когда они являются слиянием).
Операторы ^
и ~
могут быть объединены.
Ответ 6
Формат ^<n>
позволяет вам выбрать n-го родителя фиксации (релевантно в слияниях). Формат ~<n>
позволяет вам выбрать n-го предопределения, всегда следуя за первым родителем. См. git-rev-parse документацию для некоторых примеров.
Ответ 7
Стоит отметить, что git также имеет синтаксис для отслеживания "from-where-you-come" / "want-to-back-back-now" - например, [email protected]{1}
будет ссылаться на место из где вы переместились в новое местоположение фиксации.
В основном переменные [email protected]{}
фиксируют историю движения HEAD, и вы можете решить использовать определенную голову, просмотрев в reflogs git с помощью команды git reflog
.
Пример:
0aee51f [email protected]{0}: reset: moving to [email protected]{5}
290e035 [email protected]{1}: reset: moving to [email protected]{7}
0aee51f [email protected]{2}: reset: moving to [email protected]{3}
290e035 [email protected]{3}: reset: moving to [email protected]{3}
9e77426 [email protected]{4}: reset: moving to [email protected]{3}
290e035 [email protected]{5}: reset: moving to [email protected]{3}
0aee51f [email protected]{6}: reset: moving to [email protected]{3}
290e035 [email protected]{7}: reset: moving to [email protected]{3}
9e77426 [email protected]{8}: reset: moving to [email protected]{3}
290e035 [email protected]{9}: reset: moving to [email protected]{1}
0aee51f [email protected]{10}: reset: moving to [email protected]{4}
290e035 [email protected]{11}: reset: moving to HEAD^
9e77426 [email protected]{12}: reset: moving to HEAD^
eb48179 [email protected]{13}: reset: moving to HEAD~
f916d93 [email protected]{14}: reset: moving to HEAD~
0aee51f [email protected]{15}: reset: moving to [email protected]{5}
f19fd9b [email protected]{16}: reset: moving to HEAD~1
290e035 [email protected]{17}: reset: moving to HEAD~2
eb48179 [email protected]{18}: reset: moving to HEAD~2
0aee51f [email protected]{19}: reset: moving to [email protected]{5}
eb48179 [email protected]{20}: reset: moving to HEAD~2
0aee51f [email protected]{21}: reset: moving to [email protected]{1}
f916d93 [email protected]{22}: reset: moving to [email protected]{1}
0aee51f [email protected]{23}: reset: moving to [email protected]{1}
f916d93 [email protected]{24}: reset: moving to HEAD^
0aee51f [email protected]{25}: commit (amend): 3rd commmit
35a7332 [email protected]{26}: checkout: moving from temp2_new_br to temp2_new_br
35a7332 [email protected]{27}: commit (amend): 3rd commmit
72c0be8 [email protected]{28}: commit (amend): 3rd commmit
Примером может быть то, что я сделал локальные коммиты a- > b- > c- > d, а затем я вернулся, отменив 2 фиксации, чтобы проверить свой код - git reset HEAD~2
- и после этого я хочу переместить мой HEAD назад к d - git reset [email protected]{1}
.
Ответ 8
Упрощенно:
-
~
указывает предков -
^
указывает на родителей
Вы можете указать одну или несколько веток при объединении. Тогда в коммите есть два или более родителей, а затем ^
полезно указать родителей.
Предположим, что вы находитесь на ветки A, и у вас есть еще две ветки: B и C.
На каждой ветке три последних коммита:
- A: A1, A2, A3
- B: B1, B2, B3
- С: С1, С3, С3
Если сейчас на ветке А вы выполняете команду:
git merge B C
тогда вы объединяете три ветки (здесь у вашего коммита слияния три родителя)
а также
~
указывает n-го предка в первой ветки, поэтому
-
HEAD~
указывает A3 -
HEAD~2
обозначает A2 -
HEAD~3
обозначает A1
^
обозначает n-го родителя, поэтому
-
HEAD^
обозначает A3 -
HEAD^2
обозначает B3 -
HEAD^3
обозначает C3
Следующее использование ~
или ^
рядом друг с другом происходит в контексте коммита, обозначенного предыдущими символами.
Обратите внимание 1:
-
HEAD~3
всегда равно: HEAD~~~
и: HEAD^^^
(каждый обозначает А1),
и вообще:
-
HEAD~n
всегда равно: HEAD~...~
(n раз ~
) и: HEAD^...^
(n раз ^
).
Примечание 2:
-
HEAD^3
- это не то же самое, что HEAD^^^
(первое означает C3, а второе - A1),
и вообще:
-
HEAD^1
совпадает с HEAD^
, - но для n> 1:
HEAD^n
всегда не совпадает с HEAD^...^
(n раз ~
).
Ответ 9
TL;DR
~ - это то, что вы хотите большую часть времени, оно ссылается на прошлое, передает текущую ветвь
^ ссылки родителей (git -merge создает 2-го родителя и более)
А ~ всегда совпадает с А ^
A ~~ всегда совпадает с A ^^ и т.д.
А ~ 2 не совпадает с А ^ 2,
потому что ~ 2 является сокращением для ~~
в то время как ^ 2 не является сокращением для чего-либо, это означает, что второй родительский
Ответ 10
HEAD ^^^ совпадает с HEAD ~ 3, выбирая третью фиксацию перед HEAD
HEAD ^ 2 указывает вторую головку в фиксации слияния
Ответ 11
Пример:
Если вы хотите следовать боковой ветке, вам нужно указать что-то вроде
master~209^2~15
Ответ 12
фактический пример различия между HEAD ~ и HEAD ^
Ответ 13
Проще говоря, для первого уровня происхождения (родословной, наследования, родства и т.д.) 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) в иерархии, что означает фиксацию grandparent HEAD.
HEAD ^ 2, с другой стороны, ссылается не на первую родительскую вторую родительскую фиксацию, а просто на вторую родительскую фиксацию. Это связано с тем, что каретка означает родительский элемент фиксации, а следующее число означает, что/какой родительский фиксатор ссылается (первый родитель, в случае, когда за кареткой не следует число [поскольку это сокращенное число 1, что означает первый родитель]). В отличие от каретки число, которое следует после этого, не подразумевает другого уровня иерархии вверх, а скорее подразумевает, сколько уровней вбок, в иерархию, нужно найти правильный родительский (фиксация). В отличие от числа в выражении тильды, это только один родитель в иерархии, независимо от числа (сразу), следующего за кареткой. Вместо того, чтобы подниматься вверх, число трейлинг-следов подсчитывается боком для родителей по иерархии [на уровне родителей вверх, что эквивалентно количеству последовательных карманов).
Таким образом, HEAD ^ 3 равен третьему родительскому элементу HEAD commit (НЕ прадедушка, это то, что HEAD ^^^ и HEAD ~ 3 будет...).
Ответ 14
~
это означает, что родитель родитель.
^
если у него есть родители двух или более, например коммит слияния, мы можем выбрать родителя второго или другого.
так что если только одна вещь, как (HEAD ~ или HEAD ^), она имеет те же самые результаты.