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

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

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

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

6 ответов

Решение

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

git describe --exact-match HEAD

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

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

git describe --exact-match --tags HEAD

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

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

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

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

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

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

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'"

РЕДАКТИРОВАТЬ: Исправлено регулярное выражение в sed для поддержки как облегченных, так и аннотированных / подписанных тегов.

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

git-description - Показать самый последний тег, который доступен из коммита

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

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

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

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

А также git describe сортирует их так:

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

Вот краткий скрипт оболочки (протестирован в 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

Это напечатало бы все теги, которые указывают на текущий коммит.

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