Git: Как получить имя извлеченного тега, когда 2 или более тегов на одном коммите

  1. У меня есть git commit с 2 тегами, вот так: commit1-----tagA,tagB
  2. Оформить заказ "tagA" git checkout tagA
  3. Вопрос: Как получить имя тега текущей проверки? У меня есть попытка git describe, но это всегда возвращает имя "tagB", ожидайте возврата "tagA".

    Кажется git describe только сможет вернуть самое последнее имя тега, см. руководство по git

    Команда находит самый последний тег, доступный из коммита. Если тег указывает на фиксацию, отображается только тег. В противном случае он добавляет суффикс имени тега к числу дополнительных коммитов поверх помеченного объекта и сокращенному имени объекта самого последнего коммита.

Есть ли другой метод?

Цель связана с этим вопросом:

Я хочу, чтобы файл автоматически собирал номер версии из имени тега, git describe хорошо работает, когда 1 коммит с 1 тегом, но это бесполезно в вышеприведенном случае.

2 ответа

Решение

И то и другое tagA а также tagB укажите на этот конкретный коммит, так что любое имя одинаково хорошо (или одинаково плохо) в отношении git, если вы хотите проверить или посмотреть на этот конкретный коммит.

Формулировка документации "самый последний тег", возможно, вводит в заблуждение здесь (хотя, вероятно, почему вы получаете tagB как вывод).


Как получить желаемый результат

Если вы хотите знать, какое имя тега вы дали git checkoutВы можете либо сохранить это самостоятельно, либо обратиться к журналу HEAD:

8004647 HEAD@{0}: checkout: moving from master to v2.3.1

Преимущество метода reflog в том, что он уже реализован; но повторные журналы сохраняются только некоторое время (настраивается, по умолчанию 90 дней для достижимых ссылок), после чего срок их действия истекает, чтобы предотвратить их постоянный рост.


Как "git description" предлагает ответ, который вы не хотели

"Самый последний" означает, что если теги имеют связанные метки даты и времени (аннотированные теги имеют, а легкие теги - нет), то теги с более поздним временем считаются "лучше", как описано ниже.

Обратите внимание, что в любое время любая команда git может легко найти все внешние ссылки и преобразовать их в SHA-1. Чтобы увидеть, как это работает, просто запустите git for-each-ref (вы можете передать это на пейджер, как less). Вывод выглядит примерно так:

c2e8e4b9da4d007b15faa2e3d407b2fd279f0572 commit refs/heads/maint
9ab698f4000a736864c41f57fbae1e021ac27799 commit refs/heads/master
[snip]
74d2a8cf12bf102a8cedaf66736503bb3fe88dfb tag    refs/tags/v2.2.0
[more snippage]

Это ветви и теги - в этом git-хранилище (для самого git) все теги аннотированы - и соответствующие им SHA-1. Если бы был активный тайник, он бы тоже появился (под refs/stash).

В любом случае предположим, что git describe имеет в этот момент один из этих идентификаторов фиксации (SHA-1), и describe также нашел два или более имен, которые соответствуют этому идентификатору. Это могут быть просто аннотированные имена тегов, которые вы получаете без опций, или это может быть другое имя (например, имя ветви), разрешенное --all, например; но важно предположить, что существует два или более имен, причем все они указывают на один и тот же коммит.

describe Команда может попытаться запомнить все эти имена, но это не так. Вместо этого он проводит имена через своего рода турнир "два за раз", чтобы увидеть, кто из них "выигрывает соревнование":

  • Если оба имени являются аннотированными тегами, используйте отметки даты на двух именах, чтобы выбрать, какой тег оставить, а какой выбросить. Идея здесь заключается в том, что если одному и тому же коммиту присваиваются два или более аннотированных тега, то созданный позже, вероятно, "лучше" и что git describe следует использовать.
  • Если одно имя является аннотированным тегом, а другое - нет, оставьте аннотированный тег; выкинь другое имя. Другими словами, любой аннотированный тег лучше, чем любой легкий тег.
  • Если ни одно из имен не является аннотированным тегом, но одно является (легковесным) тегом, а другое - нет, сохраните тег; выбрасывать без тега. Другими словами, любой тег лучше, чем все остальное.
  • В противном случае (ни одно из имен не является тегом какого-либо рода), сохраните "самое раннее обнаруженное" имя (это зависит от внутренних механизмов обхода имен в git и, следовательно, не обязательно предсказуемо).

После того, как все имена для этого коммита оспорены друг с другом, чтобы выбрать "выигрышное имя", "победное имя" сохраняется вместе с коммитом SHA-1.


Теперь, как мы можем получить этот идентификатор фиксации в первую очередь? Ответ в том, что git describe начинается с вашего аргумента (ов):

$ git describe       # no args, means ...
$ git describe HEAD  # use HEAD to get the SHA-1

Ваши аргументы (или HEAD) превращаются в соответствующий сырой SHA-1 с помощью git rev-parse (ну, его эквивалент C-кода):

$ git rev-parse HEAD
9ab698f4000a736864c41f57fbae1e021ac27799

Затем, git describe Запускает git for-each-ref (или его эквивалент кода C), чтобы превратить все имена, которые вы разрешаете ему использовать - по умолчанию все аннотированные теги - в идентификаторы SHA-1. Если какой-либо из них соответствует этому SHA-1, они сохраняются. Если есть несколько матчей, они проходят через конкурс, чтобы выбрать одного победителя.

Для вашего конкретного случая эта часть успешна: оба tagA а также tagB точные совпадения, поэтому все останавливается после выбора выигрышной метки между этими двумя. В вашем случае это был тег, который вы не хотели.

В общем, хотя, git describe часто должен продолжать идти. Рассмотрим, например, следующие коммиты из git сам проект:

088c9a8 strbuf.h: format asciidoc code blocks as 4-space indent
aa07cac strbuf.h: drop asciidoc list formatting from API docs
6afbbdd strbuf.h: unify documentation comments beginnings
bdfdaa4 strbuf.h: integrate api-strbuf.txt documentation
eae6953 tests: correct misuses of POSIXPERM
1767c51 t/lib-httpd: switch SANITY check for NOT_ROOT
b4a56a3 "log --pretty" documentation: do not forget "tformat:"

Теперь предположим, что мы делаем один тег X указывая на совершить b4a56a3один тег Y указывая на совершить eae6953и один тег Z указывая на совершить aa07cac, Если затем мы попросим git "описать" commit 088c9a8, это может быть описано как любое из следующего:

  • коммит, который происходит через шесть шагов после коммита X, или же
  • коммит, который происходит через четыре шага после коммита Y, или же
  • коммит, который происходит один шаг после коммита Z

Выход из git describe будет использовать тег Zпотому что для перехода от "тега Z" требуется меньше шагов (commit aa07cac) на коммит (088c9a8). Как это на самом деле происходит, удивительно (возможно, излишне, хотя это ценностное суждение:-)) сложно.

Какие git describe делает здесь, чтобы пройти через (часть) граф фиксации (применяя --first-parent если указан), чтобы найти "близлежащий" коммит, который имеет имя. Порядок поиска довольно сложно описать: он частично основан на дате git logотсортированные по дате коммиты), но он останавливается рано, если встречает аннотированный тег. В противном случае он накапливает список "имен кандидатов" (используя метод конкурса, описанный выше), останавливается, когда накопил достаточно (по умолчанию 10) имен кандидатов, а затем сортирует список. Сортировка основана на "расстоянии графика", так что X, Y, а также Z все ли это легкие теги - если бы они были аннотированными, они были бы выбраны и поиск прекратилсяZ побеждает здесь.

Наконец, найдя имя, которое указывает на подходящий коммит, который на самом деле не является коммитом с заданным аргументом, git describe печатает имя, количество удаленных шагов ("глубина") и g и часть фактического SHA-1:

v2.3.3-220-g9ab698f

(хотя глубина и g может быть подавлено, и все это может потерпеть неудачу, если имена вообще не могут быть найдены или если вы запросили только точные совпадения и т. д.).

Вы можете по крайней мере получить все теги для данного коммита:

git tag --points-at HEAD

От git tag справочная страница:

--points-at <object>

Перечислять только теги данного объекта.

Оттуда вы можете извлечь тот, который вы хотите.

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