Git: Как получить имя извлеченного тега, когда 2 или более тегов на одном коммите
- У меня есть git commit с 2 тегами, вот так:
commit1-----tagA,tagB
- Оформить заказ "tagA"
git checkout tagA
Вопрос: Как получить имя тега текущей проверки? У меня есть попытка
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>
Перечислять только теги данного объекта.
Оттуда вы можете извлечь тот, который вы хотите.