wake-up-neo.com

Git - Checkout eines Remote-Tags, wenn zwei Remotes den gleichen Tagnamen haben

Ich hatte gehofft, dass dies funktionieren würde:

git checkout remote/tag_name

aber es tut nicht Das macht:

git checkout tags/tag_name

aber ich mache etwas seltsames, wo ich viele fernbedienungen habe, und ich mache mir Sorgen, was passiert, wenn zwei fernbedienungen denselben tag haben. Gibt es eine Möglichkeit, die Fernbedienung beim Auschecken des Tags anzugeben?

44
Narfanator

Zusammenfassung: Was Sie erreichen möchten, ist möglich, aber zuerst müssen Sie Remote-Tags erfinden.

Sie tun dies mit einer Reihe von Referenzspezifikationen, eine für jede Fernbedienung. Im Rest geht es darum, was diese sind, wie sie funktionieren und so weiter.


Ihre Frage fragt nach dem Auschecken eines "Remote-Tags", aber Git hat keine Remote-Tags, und dies:

aber ich mache etwas Seltsames, wo ich viele Fernbedienungen habe, und ich mache mir Sorgen darüber, was passiert, wenn zwei Fernbedienungen den gleichen Tag haben. Gibt es eine Möglichkeit, die Fernbedienung beim Auschecken des Tags anzugeben?

enthüllt (glaube ich) die Quelle Ihrer Verwirrung.

Lassen Sie uns für einen Moment zurückkommen und darüber sprechen, was Git im Allgemeinen hat, was "Referenzen" sind. Um die Idee zu festigen, enthalten bestimmte Formulare der Verweise Ihre lokalen Zweigstellennamen (master, devel, feature usw.) , "Remote Branch Names" wie Origin/master und stuff_from_bobs_computer/master sowie Tag-Namen. Dinge wie Gits "Versteck" verwenden ebenfalls Referenzen, und sogar HEAD ist eine Referenz, obwohl es eine sehr spezielle und normalerweise eine "symbolische" Referenz ist. Der Punkt hier ist, dass Git viele Formen von Referenzen hat und sie alle am Ende auf die gleiche Weise funktionieren: Ein Referenzname wird am Ende in einen dieser großen SHA-1-Werte aufgelöst, 676699a0e0cdfd97521f3524c763222f1c30a094 oder was auch immer.

Die meisten Verweise - Ausnahmen sind Dinge wie HEAD, ORIG_HEAD, MERGE_HEAD und einige andere in diesem Sinne - werden tatsächlich mit Namen geschrieben, die mit refs/ beginnen. Diese befinden sich in einer Art Verzeichnis- oder Ordnerstruktur.1 mit Unterverzeichnissen: refs/tags/ enthält Ihre Tags,2 refs/heads/ enthält alle Ihre Zweige, und refs/remotes/ enthält alle Ihre Remote-Zweige.

Die entfernten Zweige sind weiter durch den Namen der entfernten Zweige unterteilt: refs/remotes/Origin/ enthält alle Origin entfernten Zweige, während refs/remotes/stuff_from_bobs_computer/ alle stuff_from_bobs_computer entfernten Zweige enthält. Wenn Sie viele Fernbedienungen haben, befinden sich in refs/remotes/ viele Unterverzeichnisse.

Ich habe gerade erwähnt, dass Ihre Tags alle in refs/tags/ sind. Was ist mit den Tags der Fernbedienungen, allen Tags auf den verschiedenen Fernbedienungen? Nun, git hat keine "Remote-Tags". Git hat "entfernte Zweige", aber diese sind tatsächlich alle lokal. Sie werden im Repository your unter der Überschrift refs/remotes/ gespeichert.

Wenn Ihr Git eine "Gegenstelle" kontaktiert - normalerweise über git fetch remote, aber auch für Push (und den anfänglichen clone -Schritt), Ihr Git fragt den entfernten Git3 die Frage: "Welche lokalen Niederlassungen haben Sie? Was sind ihre SHA-1-Werte?" Dies ist in der Tat, wie fetch funktioniert: Als vereinfachte Beschreibung besteht der Vorgang des Abrufens darin, den entfernten Git zu fragen: "Hey, was hast du?" und es gibt Ihnen eine Reihe von Namen und SHA-1s. Ihr Git überprüft dann, ob er die gleichen SHA-1s hat. Wenn ja, ist das Gespräch beendet. wenn nicht, sagt dein Git dann "ok, ich brauche was auch immer in den Commits für diese SHA-1s ist", was sich tatsächlich als eine weitere Gruppe von SHA-1s herausstellt, und dein Git und ihre sprechen darüber, um es herauszufinden Welche Dateien und solche Sie auch benötigen, erkennen Sie an den SHA-1s. Ihr Git überträgt diese Objekte und fügt die neuen SHA-1 in Ihren refs/remotes/ ein, und zwar unter dem Namen der Gegenstelle und dann unter deren lokalen Zweignamen.

Wenn Sie mit Ihrem fetch nach Tags fragen, leistet Ihr Git etwas mehr.4 Anstatt den Git nur nach seinen Zweigen zu fragen, fragt der Git auch nach seinen Tags. Auch hier gibt Ihnen ihr Git nur eine Liste von Namen und SHA-1s. Ihr Git überträgt dann alle benötigten zugrunde liegenden Objekte und - hier ist der Schlüssel zum gesamten Problem schreibt ihre Tag-Namen in Ihren refs/tags/.

Wenn Sie also zu remote Origin wechseln und nach Tags fragen und die Meldung "Ich habe refs/tags/pinky und refs/tags/brain" erhalten, werden für Sie lokale Tags pinky und brain, in Ihrem Referenznamensraum auch refs/tags/pinky und refs/tags/brain genannt.

Jetzt gehen Sie zu Bobs Computer (der Fernbedienung mit dem Namen stuff_from_bobs_computer oben) und fragen ihn nach Tags. Er interessiert sich eher für Neurologie als für Warner Brothers and Sister, und seine Tags sind refs/tags/spinal_cord und refs/tags/brain, und der zweite ist wahrscheinlich nicht mit dem auf Origin verwandt. Oh oh!

Genau das, was hier passiert, wird etwas kompliziert.5 Aber kurz gesagt, dies ist nur eine schlechte Situation, und Sie sollten sie wahrscheinlich nach Möglichkeit vermeiden. Es gibt zwei einfache Möglichkeiten, dies zu vermeiden. Ein offensichtlicher Nachteil ist, dass man die Tags einfach nicht bekommt. Dann haben Sie keine Tag-Konflikte. Das andere ist: Halten Sie alle ihre Tags voneinander getrennt (und vielleicht auch von Ihren). Es stellt sich heraus, dass der zweite nicht wirklich so schwierig ist. Sie müssen nur entfernte Tags "erfinden".

Werfen wir einen kurzen Blick darauf, wie Git "Remote Branches" implementiert und wie fetch --tags funktioniert. Beide verwenden denselben grundlegenden Mechanismus, den git "refspecs" nennt.

In der einfachsten Form sieht eine Referenzspezifikation wie zwei Referenznamen mit einem Doppelpunkt dazwischen aus: refs/heads/master:refs/heads/master, zum Beispiel. In der Tat können Sie sogar den refs/heads/ weglassen und Git wird es für Sie tun,6 und manchmal können Sie den Doppelpunkt und den wiederholten Namen auch weglassen. Dies ist die Art von Dingen, die Sie mit git Push verwenden: git Push Origin branch bedeutet, mit Ihrem refs/heads/branch zu Origin zu pushen und es refs/heads/branch zu nennen, wenn es auch auf "ihrem" Git ankommt.

Für fetch erhalten Sie bei Remote-Verzweigungen eine Referenzspezifikation, die wie folgt aussieht:

+refs/heads/*:refs/remotes/Origin/*

Der + an der Vorderseite bedeutet "Kraft", und der * macht das Offensichtliche. Ihr Git spricht mit ihnen und bekommt eine Liste der Refs. Diejenigen, die mit refs/heads/* übereinstimmen, werden von Ihnen importiert (zusammen mit den erforderlichen Repository-Objekten) - aber dann werden sie in your repo unter Namen mit dem Stern refs/remotes/Origin/ eingefügt, und jetzt haben Sie alle "Remote-Zweige" von Origin.7

Wenn Sie git fetch --tags ausführen, fügt Ihr Git +refs/tags/*:refs/tags/* zu den von ihm verwendeten Refspecs hinzu.8 Das bringt ihre Tags rüber und setzt sie in Ihre lokalen Tags. Sie müssen fetch also nur eine Referenznummer geben, die wie folgt aussieht:

+refs/tags/*:refs/rtags/Origin/*

und plötzlich haben Sie unter refs/rtags/ einen ganz neuen Namensraum für "entfernte Tags" (in diesem Fall nur für Origin). Es ist sicher, das Force-Flag + hier zu verwenden, da Sie nur Ihre Kopie der Tags aktualisieren: Wenn sie ein Tag erzwungen verschoben (oder gelöscht und neu erstellt) haben, erzwingen Sie das Verschieben Ihrer Kopie. Möglicherweise möchten oder benötigen Sie auch das Verhalten --no-tags, das Sie durch Angabe von --no-tags in der Befehlszeile erhalten, oder lesen Sie den nächsten Abschnitt.

Das einzig praktikable Element, das Sie noch wissen müssen, ist, dass git fetch seine Standard-Refspecs für eine bestimmte Remote-Instanz aus der Git-Konfigurationsdatei erhält.9 Wenn Sie Ihre Git-Konfigurationsdatei untersuchen, wird unter jeder Fernbedienung eine fetch =-Zeile mit der Zeichenfolge +refs/heads/*:refs/remotes/remote-name/* angezeigt. Sie können so viele fetch =-Zeilen pro Fernbedienung haben, wie Sie möchten. Fügen Sie also eine hinzu, um deren Tags zu übernehmen, und fügen Sie sie in Ihren neu (neu) erfundenen Namensraum für "entfernte Tags" ein. Sie können --no-tags auch als Standard für diese Fernbedienung festlegen, indem Sie tagOpt = --no-tags in demselben Abschnitt festlegen. Siehe dieser Kommentar von user20078 für Details.

Wie bei allen Git-Befehlen, die einen Namen in einen unformatierten SHA-1 auflösen, können Sie git checkout durch vollständigen Referenznamen ausführen, um auf dem entsprechenden SHA-1 in den Modus "Detached HEAD" zu gelangen:

git checkout refs/rtag/stuff_from_bobs_computer/spinal_cord

Da Git nicht die Idee von "Remote-Tags" hat, müssen Sie die lange Form buchstabieren (siehe gitrevisions für Details).


1Tatsächlich ist es ein echtes Verzeichnis in .git/refs. Es gibt jedoch auch eine "gepackte" Form für Refs, die in .git/packed-refs auftauchen. Das gepackte Formular spart Zeit und Mühe bei Refs, die sich nicht oft ändern (oder überhaupt, wie es bei Tags üblich ist). Es wird auch ständig versucht, das "Back-End" -Speichersystem für Verweise neu zu schreiben, sodass sich an einem bestimmten Punkt vieles ändern kann. Diese Änderung ist für Windows- und Mac-Systeme erforderlich. Git ist der Ansicht, dass bei Zweig- und Tag-Namen die Groß- und Kleinschreibung beachtet wird: Sie können Zweig polish für Ihr Schuhputzmaterial und Polish für Ihre Würste festlegen. Die gepackten Versionen sind zwischen Groß- und Kleinschreibung zu unterscheiden, daher funktioniert dies. Aber die in Dateien gespeicherten Versionen sind manchmal sind nicht , also nicht!

2Ich beschreibe hier den Unterschied zwischen leichten und mit Anmerkungen versehenen Tags. Kommentierte Tags sind tatsächliche Objekte im Repository, während Lightweight-Tags Beschriftungen im Bereich refs/tags/ sind. Im Allgemeinen verfügt jedes mit Anmerkungen versehene Tag über ein entsprechendes Lightweight-Tag, sodass es für diese spezielle Verwendung gleich funktioniert.

3Es ist fast immer ein anderes Git-Repo, obwohl es jetzt Adapter für Git zu Mercurial, svn und so weiter gibt. Sie haben ihre eigenen Tricks, um sich als Git-Repos auszugeben. Auch soll diese Beschreibung nicht endgültig sein: Die tatsächliche Abfolge von Vorgängen ist eher für die Übertragungseffizienz als für die Sinnhaftigkeit für den Menschen codiert.

4Ich habe hier ein wenig über die besondere Verrücktheit von fetch und clone gesprochen, d. H. Die Versionen ohne --tags. Die Versionen mit --tags sind einfach zu erklären: Sie übertragen alle Tags mit den hier beschriebenen Refspecs - und zumindest in Git 2.10 und 2.11 erzwingt --tags auch Aktualisierungen, als ob das Force-Flag + gesetzt wäre. Wenn Sie jedoch nicht explizit --no-tags aufrufen, werden durch einfaches Abrufen (und Klonen) die Tags some überschrieben. Die hinterhältige Aufgabe besteht darin, nach Tags zu suchen, die Objekten entsprechen, die aufgrund des Abrufs eingehen, und diese (ohne Aktualisierungen zu erzwingen) zum Namensraum Ihrer (globalen) Tags hinzuzufügen. Ohne --tags überschreibt Ihr Git Ihre vorhandenen Tags nicht. Mit --tags überschreibt Ihr Git wird Ihre eigenen vorhandenen Tags, mindestens in Git 2.10, je nach den tatsächlichen Experimenten, die Anfang 2017 durchgeführt wurden.

5Ältere Versionen von Git haben "branch" -Regeln auf Tags angewendet, während Push (aber nicht unbedingt abgerufen). Dies ermöglichte eine Tag-Aktualisierung, wenn es sich um einen schnellen Vorlauf handelte, und erforderte ansonsten das Force-Flag . Neuere Versionen von git Push benötigen nur das Force-Tag. Für das fetch refspec von --tags ist das Force-Flag nicht gesetzt, es verhält sich jedoch so, als wäre es so. Ich habe nicht mit Push mit --tags experimentiert. Es gibt noch eine weitere Besonderheit von git fetch: --tags vs. --no-tags vs. explizite Referenzangaben, die damit zu tun hat, wie --Prune funktioniert. In der Dokumentation wird angegeben, dass --Prune für alle expliziten Befehlszeilen-refs/tags/-Referenzspezifikationen gilt, nicht jedoch für die implizite --tags-Referenzspezifikation. Ich habe auch nicht experimentiert, um dies zu verifizieren.

6Damit Ihr Git refs/heads/ oder refs/tags/ für Sie ausfüllt, muss Ihr Git in der Lage sein, herauszufinden welches Sie meinten. Es gibt Fälle, in denen dies der Fall ist, und Fälle, in denen dies nicht der Fall ist. Wenn Ihr Git es nicht herausfindet, erhalten Sie eine Fehlermeldung und können es erneut ausfüllen. In Skripten sollten Sie diese jedoch immer explizit ausfüllen, um ein vorhersehbareres Verhalten zu erzielen. Wenn Sie nur git Push ausführen, um einen vorhandenen Zweig zu pushen, können Sie es fast immer Ihrem Git überlassen, es herauszufinden.

7Das Weglassen des Doppelpunkts und des zweiten Namens funktioniert bei git fetch nicht so gut: Es weist Ihren Git an, Ihre eigenen Referenzen überhaupt nicht zu aktualisieren! Dies erscheint unsinnig, aber tatsächlich kann nützlich sein, da git fetch immer die spezielle Datei FETCH_HEAD schreibt. Sie können die Git-Objekt-IDs (SHA-1s) aus der speziellen Datei fischen und sehen, was abgerufen wurde. Dies ist größtenteils ein Überbleibsel aus sehr frühen Versionen von Git, bevor Remote-Tracking-Zweige erfunden wurden.

8Die von git fetch --tags und git Push --tags verwendete Referenzspezifikation ist in Git Version 2.10 intern vorkompiliert und wird von einem speziellen Fallcode verarbeitet. In dem vorkompilierten Formular ist das Flag + nicht gesetzt. Experimente haben jedoch gezeigt, dass abgerufene Tags in Git 2.10/2.11 zwangsweise aktualisiert werden. Ich erinnere mich, dass ich vor Jahren mit Git 1.x experimentiert habe und festgestellt habe, dass diese --tags-abgerufenen Tags not erzwungen aktualisiert wurden. Ich denke, dies hat sich geändert, aber das ist möglicherweise nur fehlerhaft Erinnerung. In jedem Fall möchten Sie nicht einen expliziten --tags verwenden, wenn Sie entfernte Tags (neu) erfinden.

9Tatsächlich funktionieren Spiegel so. Mit fetch = +*:* erhalten Sie beispielsweise einen reinen Abrufspiegel. Der Abrufprozess kann alle Referenzen sehen. Sie können sie mit git ls-remote selbst sehen. So funktioniert --single-branch: Wenn Sie beim Klonen --single-branch verwenden, listet Ihre Git-Konfigurationsdatei nur den einen einzelnen Zweig in der Fetch-Zeile auf. Um von einem Zweig in einen Zweig mit nur einem Zweig zu konvertieren, bearbeiten Sie die Zeile einfach so, dass sie den üblichen Glob-Mustereintrag enthält.

76
torek

1 - Holen Sie den Tag von der Fernbedienung mit: 

git fetch Origin --tags 

Oder zum Auschecken eines Tags von einer anderen Remote-Anwendung:

git fetch your_remote --tags

2 Überprüfen Sie das Tag, indem Sie es ausführen 

git checkout tags/<tag_name>

Mehr hier: Lade ein bestimmtes Tag mit Git herunter

81
Russell Fair

In meinem Fall, als ein neues Tag zum Remote-Repository hinzugefügt wurde [Ich verwende Stash], war das neue Tag nicht im Ergebnis von git tag -l verfügbar.
Aber ich konnte das neu hinzugefügte Tag mit git ls-remote --tags anzeigen.
Ich musste den folgenden Befehl ausführen, um alle neuesten Tags in mein lokales Repository zu laden:
git pull --tags Beim Ausführen von git tag -l wurden jetzt auch die neu hinzugefügten Tags angezeigt.

Um ein Tag auszuchecken, verwenden Sie:
git checkout <tag_name>

Hinweis: Es ist nur normal, git status auszuführen und eine Nachricht wie diese zu finden:
HEAD detached at tag_name

9
hipsandy

Es gibt einige Fragen in meinem Kopf:

  • Warum sollten verschiedene Fernbedienungen einen anderen Code (im selben Baum) haben?
  • Warum wirkt sich der Ferncode auf das Auschecken von Tags aus?

Die Sache ist die folgende:

Wenn Sie ein Tag mit git checkout tags/fancytag auschecken, sucht es in Ihrem aktuellen Repository (auf Ihrem Computer) nach dem passenden Tag.

Wenn Sie ein Tag von einer bestimmten Fernbedienung aus checken möchten, müssen Sie zuerst fetch (den Baum der jeweiligen Fernbedienung) und dann das Kontrollkästchen auschecken.

0