2009-10-20 1 views
34

In einer Unix- oder GNU-Scripting-Umgebung (z. B. eine Linux-Distribution, Cygwin, OSX), ist der beste Weg festzustellen, ob Der aktuelle Checkout ist ein Git-Tag. Wenn es ein Tag ist, wie kann ich den Tag-Namen bestimmen?Wie programmgesteuert bestimmen, ob der Git-Checkout ein Tag ist und wenn ja, wie lautet der Tag-Name

Eine Verwendung dieser Technik wäre die automatische Kennzeichnung einer Freigabe (wie svnversion würde mit Subversion tun).

Siehe meine verwandte Frage über programmatically detecting the Git branch.

+0

Werfen Sie einen Blick auf GIT-VERSION-GEN-Skript realisiert werden (und deren Verwendung in Makefile) in Git-Repository: http://git.kernel.org/?p=git/git.git;a=blob;f=GIT-VERSION-GEN;hb=HEAD –

+0

@jhs: Ich habe Greg Hewgill Antwort hochgestuft . Ich habe die Antwort basierend auf 'git name-rev' herabgestuft, weil sie z.B. 'some-tag ~ 5' und Antwort basierend auf der Kombination von 'git log' und' git tag -l', weil es hässlich und ineffizient ist. –

Antwort

36

Die Lösung Ihrer Frage ist

git describe --exact-match HEAD 

zu verwenden (die nur kommentierte Tags in Betracht ziehen würden, aber man sollte und wahrscheinlich Tags selbst unterzeichnet für Tagging Releases kommentierten verwenden).

Wenn Sie alle Tags prüfen wollen, auch leichte-Tags (in der Regel für die lokale Tagging verwendet werden), können Sie --tags Option:

git describe --exact-match --tags HEAD 

Aber ich glaube, Sie haben " XY problem "hier, in dem Sie Fragen über mögliche Lösung für das Problem stellen, anstatt Fragen über ein Problem zu stellen ... die eine bessere Lösung haben kann.

Die Lösung für Ihr Problem ist, einen Blick zu nehmen, wie Git es in GIT-VERSION-GEN Skript tut und wie es verwendet es in seiner Makefile.

+2

Jakub, mit allem gebührenden Respekt, sind Ihre Empfehlungen zu meinem Freilassungsprozess und Ihre Spekulationen über mein wahres Problem irrelevant. Vielen Dank für Ihre technische Lösung. – JasonSmith

+0

Leider funktioniert Ihre Lösung nicht für mich. 'git describe --exact-match HEAD' =>' fatal: kein Tag stimmt genau mit '...' überein. Ich glaube, dass Sie den Parametern '-Tags' hinzufügen müssen. – JasonSmith

+0

@jhs: Wbout 'git beschreiben --exact-match HEAD' vs' git beschreiben --exact-match --tags HEAD': Die Option '--tags' wäre nur erforderlich, wenn Sie leichte Tags verwenden. Es wird empfohlen, signierte Tags (die mit Anmerkungen versehene Tags, d. H. Tag-Objekte) sind, um Releases zu markieren. –

-2

Hier ist eine kurze Shell-Skript (in Bash getestet, nicht bestätigt, wenn es auf Asche, etc. funktioniert). Es wird die Variable git_tag auf den Namen des aktuell ausgecheckten Tags gesetzt, oder lassen Sie es leer, wenn der Checkout nicht markiert ist.

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 

Kommentar von Jakub Narębski:

Diese Lösung reduziert die über alle Tags zu Looping, und prüfen, ob sie zu corrent COMMIT-Punkt, das heißt Objekt von HEAD spitz. Mit Sanitär-Befehle, das heißt Befehle für das Scripting gemeint, kann dies geschrieben werden als:

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 

Dieses Alle Tags drucken würde, die den aktuellen begehen zeigen.

+0

Verwenden Sie "git rev-parse HEAD", um SHA-1 des aktuellen Commits zu erhalten, keine komplizierte Lösung mit git-log. Verwenden Sie git-for-each-ref anstelle einer komplizierten Lösung mit "git tag -l" und "git log" (und nicht einmal "git show"). Verwenden Sie "git describe", um die Frage zu beantworten. Verwenden Sie GIT-VERSION-GEN, um das Problem zu lösen. –

2

Eine bessere Lösung (von Greg Hewgill Antwort in der anderen Frage) wäre:

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

Wenn es „undefiniert“ gibt, dann sind Sie nicht auf einem Tag. Andernfalls wird der Tag-Name zurückgegeben. So ein Einzeiler so etwas wie meine andere Antwort zu tun wäre:

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

Interactive Shell Beispiel dafür, wie es funktioniert:

$ 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 
+2

Dies würde nicht korrekt funktionieren, wenn HEAD ** vom Tag ** erreichbar ist (z. B. über einen anderen Zweig, der im Voraus zum aktuell ausgecheckten Zweig gehört), aber es ist nicht auf einem Tag selbst. Sie würden Ergebnis erhalten wie: "einige-Tag ~ 3" –

+0

Auch, um pingelig zu sein, könnte es ein Tag namens "undefined" ... –

5

Der beste Weg, dies zu tun ist, um den git describe Befehl zu verwenden:

git-beschreiben - den letzten Tag, das von einem Commit erreichbar ist

der Befehl findet die Das neueste Tag, das von einem Commit erreichbar ist. Wenn das Tag auf das Commit zeigt, wird nur das Tag angezeigt. Andernfalls wird der Tag-Name mit der Anzahl zusätzlicher Commits über dem markierten Objekt und dem abgekürzten Objektnamen des letzten Commits versehen.

+0

Vielen Dank. git-describe scheint im Vergleich zu git-name-rev nicht so einfach in einem Shell-Skript für dieses spezifische Ziel zu sein. – JasonSmith

+0

Ich sage das, weil erstens git-Fehler beschreiben, wenn sie auf einem Zweig ohne Argumente ausgeführt werden. Würden Sie 'git-describe --tags' weiterempfehlen? Aber noch wichtiger ist es nicht trivial zu sagen, ob Sie auf einem richtigen Tag sind oder nicht. Sie müssten die Ausgabe analysieren und nach Bindestrichen suchen. Aber was ist, wenn der Tag-Name hängenden hat? Aus diesem Grund bevorzuge ich 'git name-ref - nur-name -tags HEAD' – JasonSmith

+0

Fair genug, ich nehme an, die beste Option hängt von Ihrer speziellen Anwendung ab. Ich habe 'git describe' in der Vergangenheit verwendet, um ein Release automatisch zu kennzeichnen. –

4

Verwendung von git-name-rev ist für Skripte bevorzugt, da es ein Teil von git Sanitär ist, während git-beschreiben Teil Porzellan ist.

Verwenden Sie diesen Befehl, um den Namen des Tags zu drucken, wenn der HEAD auf eins zeigt, sonst nichts.

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

Hinweis die Umleitung von stderr auf/dev/null - sonst erhalten Sie eine Fehlermeldung, die besagt:

fatal: cannot describe 'SOMESHA'" 

EDIT: die Regex in sed Fest sowohl lighweight und mit Anmerkungen versehen unterstützen/signierte Tags.

2

Sie können nicht feststellen, ob der aktuelle Checkout "ein Tag ist". Sie können nur bestimmen, ob das aktuelle Auschecken ein Commit mit Tags war.

Der Unterschied ist: wenn es mehrere Tags gibt, die auf dieses commit zeigen, kann git dir nicht sagen, welches du zur Prüfung benutztest, noch wenn du wirklich eins benutzt hast, um dort überhaupt zu kommen.

Jakub’s answer here basierend auf git describe --exact-match (--tags) gibt Ihnen „die erste“ aller (kommentierte) Tags.

Und git describe sortiert sie wie folgt aus:

  • kommentierten Tags erste
    • sortiert jüngste zuerst
  • leichte Tags kommen danach
    • binär von Tag-Namen sortiert (das bedeutet alphabetisch, wenn es in Englisch kodiert ist ASCII)
    • git speichert keine Meta-Daten mit leichten Tags, so „jüngste zuerst“ kann nicht
Verwandte Themen