Почему мой тег не отображается при оформлении заказа в Git GUI?

У меня есть локальный репозиторий Git, который содержит три аннотированных тега: v0.1.0, v0.1.1, а также v0.1.2,

Когда я просматриваю историю моего проекта с gitk (Репозиторий → Визуализация истории мастера), я вижу каждый тег, назначенный для правильного коммита.

История проекта в gitk

Тем не менее, когда я пытаюсь оформить свои теги в Git GUI (Филиал → Оформить заказ... → Теги), тег для v0.1.1 не появляется

Список тегов Git GUI

Когда я пошел проверять каждый тег в gitk, я заметил, что детали тега немного отличались. Детали для v0.1.0 а также v0.1.2 перечислил их как type commit в то время как тег для v0.1.1 был указан как type tag,

Подробности по тегу v0.1.1

Стоит отметить, что я переписал историю по этому тегу. Чтобы исправить опечатку в описании моего тега, я отредактировал сообщение тега, используя 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 работает.)

Другие вопросы по тегам