Как программно определить, является ли проверка Git тегом, и если да, то каково имя тега

В среде сценариев Unix или GNU (например, дистрибутив Linux, Cygwin, OSX), какой способ лучше определить, является ли текущий контроль тегом Git. Если это тег, как определить имя тега?

Одно использование этого метода будет автоматически помечать выпуск (например, svnversion будет делать с Subversion).

Смотрите связанный с нами вопрос о программном обнаружении ветки Git.

Ответ 1

Решение вашего вопроса заключается в использовании

git describe --exact-match HEAD

(который рассмотрит только аннотированные теги, но вы должны использовать аннотированные и, возможно, даже подписанные теги для релизов тегов).

Если вы хотите рассмотреть все теги, а также легкие теги (которые обычно используются для локальной маркировки), вы можете использовать опцию --tags:

git describe --exact-match --tags HEAD

Но я думаю, что у вас есть проблема XY", здесь вы задаете вопрос о возможном решении проблемы, а не задаете вопрос о проблеме... которая может иметь лучшее решение.

Решение вашей проблемы должно выглядеть так: Git делает это в GIT-VERSION-GEN script, и как он использует его в заголовке Makefile.

Ответ 2

Лучший способ сделать это - использовать команду git describe:

git -describe - Показать последний тег, доступный из фиксации

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

Ответ 3

Использование сценария git -name-rev является предпочтительным для скриптов, поскольку оно является частью git сантехники, тогда как git -describe является частью фарфора.

Используйте эту команду для печати имени тега, если HEAD указывает на один, в противном случае ничего.

git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed -n 's/^\([^^~]\{1,\}\)\(\^0\)\{0,1\}$/\1/p'

Примечание перенаправление stderr на /dev/null - в противном случае вы получите сообщение об ошибке:

fatal: cannot describe 'SOMESHA'"

EDIT: Исправлено регулярное выражение в sed для поддержки как взвешенных, так и аннотированных/подписанных тегов.

Ответ 4

Лучшее решение (от ответа Грега Хьюглилла в другом вопросе):

git name-rev --name-only --tags HEAD

Если он возвращает "undefined", то вы не используете тег. В противном случае он возвращает имя тега. Таким образом, один-лайнер, чтобы сделать что-то вроде моего другого ответа, будет следующим:

git_tag=`git name-rev --name-only --tags HEAD | sed 's/^undefined$//'`

Интерактивный пример оболочки, как это работает:

$ git checkout master
Already on "master"
$ git name-rev --name-only --tags HEAD
undefined
$ git checkout some-tag
Note: moving to "some-tag" which isnt a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 1234567... Some comment blah blah
$ git name-rev --name-only --tags HEAD
some-tag

Ответ 5

Вы не можете определить, является ли текущая проверка "тегом". Вы можете определить, действительно ли текущий контроль был фиксацией с тегами.

Разница заключается в следующем: если есть несколько тегов, указывающих на это commit, git не могу сказать, что вы использовали для проверки, а также если вы действительно использовали его, чтобы добраться туда вообще.

Ответ Jakubs здесь на основе git describe --exact-match (--tags) дает вам "первый" из всех (аннотированных) тегов.

И git describe сортирует их следующим образом:

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

Ответ 6

Вот краткая оболочка script (проверена в Bash, не подтверждена, если она работает на золу и т.д.). Он установит переменную git_tag в имя текущего метка, либо оставьте поле пустым, если проверка не помечена.

git_tag=''
this_commit=`git log --pretty=format:%H%n HEAD^..`

for tag in `git tag -l`; do
  tag_commit=`git log --pretty=format:%H%n tags/$tag^..tags/$tag`
  if [ "$this_commit" = "$tag_commit" ]; then
    # This is a tagged commit, so use the tag.
    git_tag="$tag"
  fi
done

Комментарий Якуба Нарбского:

Это решение сводится к циклу над всеми тегами и проверяет, указывают ли они на фиксацию корня, т.е. объект, на который указывает HEAD. Используя команды сантехники, то есть команды, предназначенные для сценариев, это можно записать как:

this_commit=$(git rev-parse --verify HEAD)
git for-each-ref --format="%(*objectname) %(refname:short)" refs/tags/ |
while read tagged_object tagname
do
    if test "$this_commit" = "$tagged_object"
    then
        echo $tagname
    fi
done

Это приведет к печати всех тегов, которые указывают на текущую фиксацию.