Почему мой тег не отображается при оформлении заказа в Git GUI?
У меня есть локальный репозиторий Git, который содержит три аннотированных тега: v0.1.0
, v0.1.1
, а также v0.1.2
,
Когда я просматриваю историю моего проекта с gitk
(Репозиторий → Визуализация истории мастера), я вижу каждый тег, назначенный для правильного коммита.
Тем не менее, когда я пытаюсь оформить свои теги в Git GUI (Филиал → Оформить заказ... → Теги), тег для v0.1.1
не появляется
Когда я пошел проверять каждый тег в gitk, я заметил, что детали тега немного отличались. Детали для v0.1.0
а также v0.1.2
перечислил их как type commit
в то время как тег для v0.1.1
был указан как type tag
,
Стоит отметить, что я переписал историю по этому тегу. Чтобы исправить опечатку в описании моего тега, я отредактировал сообщение тега, используя git tag <tag name> <tag name> -f -m "<new message>"
,
Почему я не вижу свой v0.1.1
тег при проверке с Git GUI? Почему это выглядит как type tag
?
2 ответа
Теги могут указывать на любой объект в репозитории git. Если ваш тип тега "tag", то у вас есть тег, указывающий на другой тег.
Легкие теги не являются объектами; таким образом, у них нет собственного хеш-идентификатора, и ничто иное (как другой тег) не может указывать на них. Это буквально простые для запоминания имена, указывающие на хэш-идентификатор какого-либо объекта, чуть меньше, чем имя ветви.
Тем не менее, аннотированные теги являются объектами; они похожи на коммиты, со своим собственным сообщением, автором, датой создания и, что самое важное, со своим собственным хеш-идентификатором. Это означает, что в некотором роде они могут быть помечены.
Конечно же, как вы описали в своем комментарии, это именно то, что произошло. Действуя по совету, найденному в разделе Как переименовать тег Git? Вы сделали следующее:
# avoid this...
git tag new old
поскольку old
был аннотированный тег, цель для new
тег будет old
тег, а не коммит, на который он указывал.
Если вы хотите переименовать аннотированный тег, вы должны использовать
git tag -a new old^{}
old^{}
будет рекурсивно разыменовывать тег до тех пор, пока не будет найден объект без тега (в нашем случае, с фиксацией), и использовать его в качестве целевого объекта для new
,
Для дальнейшей иллюстрации: допустим, у вас есть репо... о, вот так: https://github.com/cyborgx37/sandbox/releases
В этом репо вы создаете аннотированный тег примерно так:
> git tag -m "Version 0.1-beat" v0.1
Ох, стреляйте... вы неправильно написали "бета", а также решили, что хотите, чтобы имя тега было v0.1-b
, Так как это уже было опубликовано, вы решаете заняться вменяемым делом и просто создаете новый тег. Следуя совету, который вы нашли в Интернете, вы создаете тег, который вам действительно нужен (я добавил __tag
по причинам, которые станут понятны) путем копирования первого тега:
> git tag -m "Version 0.1-beta" v0.1-b__tag v0.1
Только это аннотированные теги, то есть они являются реальными объектами. Итак, когда вы создали v0.1-b__tag
Вы на самом деле указали на v0.1
, Вы можете ясно увидеть результат, используя cat-file
а также show
,
Вот v0.1
:
> git cat-file -p v0.1
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1
tagger JDB <jd@domain.com> 1521058797 -0400
Version 0.1-beat
> git show v0.1
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
Заметить, что v0.1-b__tag
отличается как по своему типу цели, так и по своей истории:
> git cat-file -p v0.1-b__tag
object 889b82584b2294486f4956dfea17b05e6224fb7f
type tag
tag v0.1-b__tag
tagger JDB <jd@domain.com> 1521059058 -0400
Version 0.1-beta
> git show v0.1-b__tag
tag v0.1-b__tag
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:24:18 2018 -0400
Version 0.1-beta
tag v0.1
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:19:57 2018 -0400
Version 0.1-beat
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
Очевидно, Git GUI довольно избирательно относится к тому, какие типы объектов можно извлекать (коммиты, а не теги), поэтому он игнорирует ваш тег, указывающий на другой тег.
Если вы используете git tag -a new old^{}
Подход, который я предложил выше, вы можете избежать драмы и получить то, что вы хотели в первую очередь. Я создам новый тег, v0.1-b__commit
это указывает на v0.1
, а не v0.1
непосредственно:
> git tag -m "Version 0.1-beta" v0.1-b__commit v0.1^{}
> git cat-file -p v0.1-b__commit
object 5cf4de319291579d4416da8e0eba8a2973f8b0cf
type commit
tag v0.1-b__commit
tagger JDB <jd@domain.com> 1521059039 -0400
Version 0.1-beta
> git show v0.1-b__commit
tag v0.1-b__commit
Tagger: JDB <jd@domain.com>
Date: Wed Mar 14 16:23:59 2018 -0400
Version 0.1-beta
commit 5cf4de319291579d4416da8e0eba8a2973f8b0cf (HEAD -> master, tag: v0.1-b__tag, tag: v0.1-b__commit, tag: v0.1, origin/master)
Author: JDB <jd@domain.com>
Date: Tue Oct 10 12:17:00 2017 -0400
add gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42d9955
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+file.txt
Я обычно не использую ни один из графических интерфейсов Git, поэтому я не могу ответить на конкретные части графического интерфейса, но вы заметили, что здесь есть разница между аннотированными тегами и облегченными тегами, и да, должно быть некоторые предупреждения в некоторых ответах на Как вы переименуете тег Git?
Когда я пошел проверять каждый тег в gitk, я заметил, что детали тега немного отличались. Детали для
v0.1.0
а такжеv0.1.2
перечислил их какtype commit
в то время как тег дляv0.1.1
был указан как тип тега. Я подозреваю, что это может быть причиной моей проблемы...
Давайте выясним разницу между ними и поговорим о механизмах, стоящих за тегами.
В Git "истинное имя" любого фактического коммита - это идентификатор хэша коммита. Хэш-идентификаторы - это длинные, некрасивые, запоминающиеся строки, такие как ca5728b6...
показывая в одной из ваших панелей GUI. Я сделал новый пустой репозиторий и сделал в нем один коммит:
$ git init
Initialized empty Git repository in ...
$ echo for testing tags > README
$ git add README
$ git commit -m initial
[master (root-commit) a912caa] initial
1 file changed, 1 insertion(+)
create mode 100644 README
$ git rev-parse HEAD
a912caa83de69ef8e5e3e06c3d74b6c409068572
Это идентифицирует коммит, и мы можем видеть, что используя git cat-file -t
, который говорит нам о типе каждого внутреннего объекта Git:
$ git cat-file -t a912c
commit
Вы можете сократить большие уродливые идентификаторы, если аббревиатура уникальна и состоит как минимум из четырех букв. 1
В любом случае, теперь давайте сделаем два разных тега, указывающих на этот же коммит:
$ git tag -m "an annotated tag" annotag
$ git tag lightweight
и использовать git for-each-ref
осмотреть их:
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
Аннотированный тег имеет хеш-идентификатор, отличный от облегченного тега.
Хитрость в том, что легкий тег создает только имя в справочной базе данных, в данном случае, refs/tags/lightweight
, Имена в ссылочных хеш-идентификаторах хранилища базы данных, так что в этом хранится хеш-идентификатор нашего отдельного коммита.
С другой стороны, аннотированный тег существует как реальный объект репозитория, поэтому мы можем проверить его тип и увидеть его содержимое, используя git cat-file
:
$ git cat-file -t dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
tag
$ git cat-file -p dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
Обратите внимание, что аннотированный объект тега в базе данных репозитория, идентифицируемой по хеш-идентификатору и содержащий данные объекта, содержит хеш-идентификатор фиксации. В сущности, есть также "облегченный" тег с именем refs/tags/annotag
указывая на аннотированный тег объекта. Но так как он указывает на аннотированный тег-объект, он рассматривается как аннотированный тег.
Когда вы создаете новый тег, вы можете указать его на любой существующий объект. Давайте посмотрим на объекты, связанные с одним коммитом:
$ git cat-file -p HEAD | sed 's/@/ /'
tree 4d73be7092200632865da23347ba0af4ac6c91f7
author Chris Torek <chris.torek gmail.com> 1521053169 -0700
committer Chris Torek <chris.torek gmail.com> 1521053169 -0700
initial
Этот объект коммита ссылается на объект дерева, который мы можем проверить:
$ git cat-file -p 4d73be7092200632865da23347ba0af4ac6c91f7
100644 blob 938c7cff87a9b753ae70d91412d3ead5c95ef932 README
и дерево указывает на объект blob, который мы также можем проверить:
$ git cat-file -p 938c7cff87a9b753ae70d91412d3ead5c95ef932
for testing tags
который является содержимым файла README
, Давайте отметим это:
$ git tag the-file 938c7cff87a9b753ae70d91412d3ead5c95ef932
и проверить его тип:
$ git cat-file -t the-file
blob
Это не обычное использование тега, но это разрешено. Давайте попробуем сделать облегченный тег для аннотированного тега:
$ git tag maybe-light annotag
$ git cat-file -t maybe-light
tag
$ git cat-file -p maybe-light | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag annotag
tagger Chris Torek <chris.torek gmail.com> 1521059496 -0700
an annotated tag
это maybe-light
тег указывает на объект аннотированного тега, который принадлежит аннотированному тегу annotag
, Является maybe-light
аннотированный тег? Это зависит от вашей точки зрения, не так ли? Я бы сказал, что это и есть, и нет: это легкий тег, указывающий на аннотированный тег, но это не легкий тег, который имеет то же имя, что и объект аннотированного тега, который утверждает, что прямо внутри объекта он является / принадлежит -в annotag
, Но я бы также сказал, что annotag
это легкий и аннотированный тег: это легкий тег, который дает идентификатор объекта аннотированного тега. Они используют одно и то же имя, поэтому я бы назвал его "аннотированным тегом" и ссылался на refs/tags/annotag
как имя тега, так же refs/tags/maybe-light
это имя тега.
В любом случае мы также можем сделать больше аннотированных тегов, указывающих на любой из этих объектов. Если мы создадим аннотированный тег, указывающий на другой аннотированный тег, мы получим два аннотированных теговых объекта в хранилище:
$ git tag -m "also annotated" anno2
$ git for-each-ref
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/heads/master
060527046d210f0219170cdc6354afe4834ddc6d tag refs/tags/anno2
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/annotag
a912caa83de69ef8e5e3e06c3d74b6c409068572 commit refs/tags/lightweight
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356 tag refs/tags/maybe-light
938c7cff87a9b753ae70d91412d3ead5c95ef932 blob refs/tags/the-file
Из этого видно, что anno2
есть новый объект, 0605...
:
$ git cat-file -p 0605 | sed 's/@/ /'
object a912caa83de69ef8e5e3e06c3d74b6c409068572
type commit
tag anno2
tagger Chris Torek <chris.torek gmail.com> 1521060518 -0700
also annotated
В то же время, git for-each-ref
описывает maybe-light
пометить как tag
а не commit
: это просто говорит нам о том, что его непосредственный целевой объект, без перехода к другим объектам, является тегом, а не коммитом.
Давайте сделаем еще один аннотированный тег для BLOB-объекта:
$ git tag -m "annotated blob" annoblob the-file
Так как это аннотированный тег, git for-each-ref
говорит, что его тип tag
(попытайся!).
Git называет процесс следования тегу до его конечного объекта "очисткой тега", и для этого существует специальный синтаксис:
$ git rev-parse annotag annotag^{} annoblob annoblob^{}
dc4695ffede0a877fdc61dc06f5ad5c6d5cfc356
a912caa83de69ef8e5e3e06c3d74b6c409068572
398b3b89e0377b8942e2f84c97a24afaad0dccb0
938c7cff87a9b753ae70d91412d3ead5c95ef932
Обратите внимание, что это отличается от простого следования тегу один раз, как мы видим, если мы анализируем anno2
сюда:
$ git rev-parse anno2^{}
a912caa83de69ef8e5e3e06c3d74b6c409068572
a912...
это идентификатор коммита, а не второй аннотированный тег. Сравнить с:
$ git rev-parse anno2 anno2^{tag}
060527046d210f0219170cdc6354afe4834ddc6d
060527046d210f0219170cdc6354afe4834ddc6d
Первый находит идентификатор объекта, к которому anno2
точки; вторая проверяет, что это объект базы данных типа tag
, Оба, конечно, имеют одинаковый идентификатор, и это действительно объект типа tag
, Мы можем специально запросить коммит:
$ git rev-parse anno2^{commit}
a912caa83de69ef8e5e3e06c3d74b6c409068572
но если мы сделаем это с именем annoblob
мы получаем ошибку:
$ git rev-parse annoblob^{commit}
error: annoblob^{commit}: expected commit type, but the object
dereferences to blob type
вот почему ^{}
синтаксис существует: это означает, что следуйте тегам, пока вы не достигнете не тега, что бы это ни было.
1 Ограничение в четыре символа означает, что если вы назовете ветку cab
, ты в порядке. Если вы назовете это face
Тем не менее, это имя ветви или необработанный хэш-идентификатор? Что если это может быть больше, чем одно? Смотрите подсказки в документации gitrevisions, но ответ таков: это зависит от команды. Если вы изложите ссылку, refs/heads/face
или даже просто heads/face
больше не похож ни на название ветви, ни на сокращенный идентификатор хеша. к несчастью git checkout
требует безымянного имени face
(но всегда обрабатывает это как название ветви, если это возможно).
Резюме
Имя тега - это просто имя в refs/tags/
имя-пространства. git tag
Команда может создавать новые имена тегов. Это имя должно указывать на некоторый хэш-идентификатор; идентификатор может быть идентификатором любого существующего объекта, или вы можете иметь git tag
сделать новый объект тега.
Объект тега или объект аннотированного тега - это объект в базе данных репозитория. Он имеет уникальный хэш-идентификатор, как и коммит. Имеет тип tag
(против коммита, который имеет тип commit
). Его метаданные состоят из целевого объекта, имени тегера, имени тега, любого сообщения, которое вам нравится, и необязательной подписи PGP.
Целевым объектом тегового объекта является любой существующий объект в базе данных репозитория. Этот объект должен существовать при создании объекта тега. Это не позволяет аннотированному тегу указывать на себя или на объект тега, который вы еще не создали, что предотвращает циклы на графике.
Бег git tag
создание нового тега создает либо просто имя тега, указывающее на некоторый существующий объект, либо имя тега, указывающее на новый объект тега, указывающий на некоторый существующий объект. Существующий объект, каким бы он ни был, продолжает существовать.
Бег git tag -d
удаляет только имя тега. Объект тега, если он есть, остается в хранилище. Подобно объектам коммитов, он в конечном итоге будет собираться и отбрасываться, если и только если нет других ссылок, по которым можно добраться до объекта тега. (Это случается когда-нибудь в будущем, когда git gc
работает.)