Ein altes Commit auschecken und den Kopf in der Hauptniederlassung behalten?


85

Derzeit führe ich den Befehl aus, um zu einem anderen Git-Commit zu wechseln (im selben Zweig ... eigentlich im Hauptzweig!)

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Jetzt sagt mir jedes Mal, wenn ich diesen Idioten mache, dass ich jetzt einen abgetrennten Kopf habe. Wie gehe ich zu einem älteren Commit und behalte den Kopf immer noch im selben Zweig?


1
Nachdem Sie diesen Befehl ausgeführt haben, ändert sich Ihre HEAD-Referenz zu diesem Commit. Es macht keinen Sinn zu wollen, dass der KOPF auch woanders hin zeigt.
Greg Hewgill

Nach dem, was ich aus der Botschaft von git verstehe, zeigt es nirgendwo hin , was unerwünscht ist.
verschlungenes Elysium

1
Die Nachricht Git zeigt , wenn Sie eine bestimmte überprüfen begehen wie das sagt „HEAD ist jetzt bei ea3d5ed ...“, das Ihnen sagt , dass HEAD ist irgendwo zeigt. Es zeigt nur auf eine Stelle, die außer HEAD keinen anderen Namen hat (im Moment git checkoutwird HEAD durch einen anderen Commit- oder Zweignamen an diesen neuen Ort verschoben).
Greg Hewgill

Erklären die gegebenen Antworten dies angemessen, oder gibt es etwas, über das wir klarer sein könnten? Gerne kläre ich meine Antwort für Sie, wenn sie Ihre Frage nicht beantwortet.
Brian Campbell

Wenn Sie hierher gekommen sind, um nach einer Möglichkeit zu suchen, ein anderes Commit auszuchecken, während der HEAD vollständig unverändert git revert --no-commit 0766c053..HEADbleibt (z. B. um zu einem älteren Commit zurückzukehren): Führen Sie dies aus, wo 0766c053sich das Commit befindet, das Sie auschecken möchten. Dies ist von stackoverflow.com/a/21718540/525872 .
Jo Liss

Antworten:


193

Die meiste Zeit, wenn ich das mache, checke ich zu einem temporären Zweig aus:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Nachdem ich fertig bin, lösche ich einfach den Zweig


80

Dies hängt davon ab, was Sie beim Auschecken dieses Commits tun möchten. Wenn Sie es nur auschecken, um diese Revision zu erstellen oder zu testen, ist es nichts Falsches, mit einem abgenommenen Kopf zu arbeiten. Denken Sie daran, einen tatsächlichen Zweig zu überprüfen, bevor Sie Commits vornehmen (git checkout master , z. B.), damit Sie keine Commits erstellen, die in keinem Zweig enthalten sind.

Wenn Sie jedoch ab diesem Punkt mehr Commits durchführen möchten, sollten Sie einen Zweig erstellen. Wenn Sie Commits ausführen, auf die nicht von einem Zweig verwiesen wird, können diese leicht verloren gehen und werden schließlich vom Garbage Collector von git bereinigt, da sich nichts auf sie bezieht. Sie können einen neuen Zweig erstellen, indem Sie Folgendes ausführen:

git checkout -b newbranch ea3d5ed

Zur besseren Veranschaulichung finden Sie hier einige Diagramme, die zeigen, wie sich die Arbeit an einem abgetrennten Kopf von der Arbeit an einem Zweig unterscheidet.

Beginnen wir mit 3 Commits auf master, A, B und C. masterist der aktuelle Zweig, zeigt also HEADaufmaster die Punkte, auf die C festgeschrieben werden soll.

ABC
* - * - * <- master <- HEAD

Wenn wir jetzt ein Commit durchführen, erstellt git ein Commit mit C als übergeordnetem Element (da dies das aktuelle Commit ist, auf das von HEADvia verwiesen wird master) und wird aktualisiert master, um auf dieses neue Commit zu verweisen. Alle unsere Commits sind jetzt in masterund HEADverweisen auf das neue Commit durch master.

A B C D
* - * - * - * <- master <- HEAD

Schauen wir uns jetzt B an und geben uns eine freistehende HEAD.

A B C D
* - * - * - * <- master
   ^
    \-- KOPF

Hier funktioniert alles gut; Wir können uns alle Dateien ansehen, unser Programm erstellen, es testen usw. Wir können sogar neue Commits erstellen. Wenn wir dies tun, gibt es keinen Zweig, in dem wir uns befinden, sodass wir keinen Zweig auf dieses neue Commit verweisen können. Das einzige, was darauf hinweist, ist HEAD:

A B C D
* - * - * - * <- master
    \.
     * <- KOPF
     E.

Wenn wir uns später dazu entschließen, mastererneut auszuchecken, wird sich nichts auf E beziehen.

A B C D
* - * - * - * <- master <- HEAD
    \.
     * *
     E.

Da sich nichts darauf bezieht, kann es schwierig sein, sie zu finden, und git betrachtet Commits ohne Verweise als abgebrochen (sie treten häufig auf, wenn Sie Patches neu erstellen, Squash-Squashs ausführen oder andere lustige Verlaufsmanipulationen durchführen; sie stellen normalerweise verlassene Patches dar; das interessiert dich nicht mehr). Nach einer bestimmten Zeit betrachtet git es als Müll, der beim nächsten Ausführen der Garbage Collection verworfen wird.

Anstatt eine bloße Revision auszuchecken und einen abgetrennten Kopf zu erhalten, sollten Sie, wenn Sie das Gefühl haben, mehr Commits zu machen git checkout -b branch B, einen Zweig erstellen und ihn auschecken. Jetzt gehen Ihre Commits nicht verloren, da sie in einem Zweig enthalten sind, auf den Sie leicht verweisen und der später zusammengeführt werden kann.

A B C D
* - * - * - * <- master
   ^
    \ - branch <- HEAD

Wenn Sie dies vergessen und Commits außerhalb eines Zweigs erstellen, müssen Sie sich keine Sorgen machen. Sie können einen Zweig erstellen, der auf die Kopfrevision mit verweist git checkout -b branch. Wenn Sie bereits wieder in den masterZweig gewechselt sind und feststellen, dass Sie ein Streu-Commit vergessen haben, können Sie es mithilfe von git reflogfinden. Dies zeigt Ihnen einen Verlauf dessen, worauf Commits HEADin den letzten Tagen hingewiesen haben. Alles, was sich noch im Reflog befindet, wird nicht als Müll gesammelt, und Referenzen werden im Allgemeinen mindestens 30 Tage lang im Reflog aufbewahrt.


Mir ist nicht ganz klar, warum sich der Kopf beim Auschecken eines alten Commits löst . Vielleicht liegt das Problem darin, was ein losgelöster Kopf bedeutet? Auf der anderen Seite, wenn das Auschecken eines alten Commits mit abgenommenem Kopf nur verloren geht, warum sollte es dann jemand tun? Nur um herumzuspielen und Dinge auszuprobieren? Warum erlaubt Git das überhaupt?
verschlungenes Elysium

5
@devoured elysium "losgelöster Kopf" bedeutet, dass Sie einen HEADRef haben, der direkt auf einen SHA-1 eines Commits zeigt, anstatt auf einen Zweig, der wiederum auf einen Commit zeigt. Da Ihr Kopf nicht auf einen Zweig verweist, weiß Git nicht, welcher Zweig aktualisiert werden soll, wenn Sie neue Commits hinzufügen. Wie ich zu Beginn meiner Antwort erklärt habe, ist es vollkommen in Ordnung, einen abgetrennten Kopf zu haben, wenn Sie zu einer alten Version zurückkehren, nur um den Code zu erstellen oder zu testen. Sie können jederzeit zu einer Niederlassung mit git checkout masteroder dergleichen zurückkehren. Es ist nur ein Problem, wenn Sie sich verpflichten, während Sie einen abgetrennten Kopf haben.
Brian Campbell

@BrianCampbell - Nachdem Sie Commits für den Zweig (wo sich Ihr Kopf gerade befindet) vorgenommen haben, führen Sie den Zweig in B und B in den Master ein. Was solltest du als nächstes tun?
amey1908

Ihre Erklärung hat eine Reihe anderer Dinge für mich zum "Klicken" gebracht. Danke dir. Ich könnte jetzt endlich git verstanden haben ...
Joe

8

Wenn Sie einfach zu einem früheren Commit zurückkehren möchten, um damit zu spielen, ohne Änderungen vorzunehmen, können Sie dies tun

git co <previous-commit-id>

Nach diesem Befehl befinden Sie sich in einem Zweig mit dem Namen "(kein Zweig)".

Bestätigen Sie dies mit

git br

Nachdem Sie mit diesem zuvor festgeschriebenen Code gespielt haben, können Sie zu dem Zweig wechseln, in dem Sie sich befanden

git co <the-branch-you-were-on>

Das "(kein Zweig)" wird automatisch gelöscht. Auf diese Weise müssen Sie keinen temporären Zweig erstellen.


5

Gits HEAD ist einfach ein Zeiger, der sagt, was sich im Arbeitsverzeichnis befindet. Wenn Sie ein Commit auschecken möchten, das nicht der Leiter eines Zweigs ist, müssen Sie einfach Ihren HEAD umleiten, um auf dieses Commit zu verweisen. Daran führt kein Weg vorbei. Sie können bei diesem Commit einen temporären Zweig erstellen, aber HEAD wird trotzdem vom Master weggeleitet.

Das ist die kurze Erklärung. Die folgende Ausführlichkeit hilft hoffentlich dabei, zu verstehen, wie sich HEAD und Master unterscheiden:

Normalerweise sieht es so aus:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

Das heißt: „Das übergeordnete Element von C ist B und das übergeordnete Element von B ist A. Der Zweigmaster zeigt auf C, und ich habe derzeit den Inhalt des Masters überprüft. Wenn ich mich verpflichte, wird der Master außerdem aktualisiert. “

Darin sind einige Annahmen enthalten, die für ein gründliches Verständnis des Festschreibungsgraphen erforderlich sind. Commits beziehen sich nämlich nur auf ihre Eltern, und der Inhalt eines Zweigs sind jene Commits (und nur jene Commits), die durch Folgen der übergeordneten Links erreicht werden können. Der (unveränderte) Inhalt des Arbeitsbaums und des Index muss dem von HEAD benannten Commit entsprechen, entweder indirekt ("symbolisch") oder direkt ("getrennt").

Wenn Sie also ein altes Commit auschecken möchten, muss der HEAD aktualisiert werden, um auf das gewünschte Commit zu verweisen. git-checkoutmacht genau das:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Jetzt haben Sie Ihren Zweig hinter sich gelassen, da Sie etwas Altes betrachten. Das ist vollkommen in Ordnung, wie der Ratschlag „losgelöster Kopf“ Ihnen ruhig sagt (Hervorhebung von mir):

Sie können sich umschauen, experimentelle Änderungen vornehmen und diese festschreiben, und Sie können alle in diesem Zustand vorgenommenen Festschreibungen verwerfen, ohne Auswirkungen auf Zweige zu haben, indem Sie eine weitere Prüfung durchführen.

Auf der anderen Seite hat das Zurücksetzen Ihres Zweigs auch HEAD dort, wo es sein muss, aber es hätte einen ganz anderen Effekt!

C
↓
B ← refs/heads/master ← HEAD
↓
A

Das Commit C wird zu Müll, da Sie erklärt haben, dass Sie nicht mehr möchten, dass es Teil des Hauptzweigs ist.

Kurz gesagt, alles, was Sie tun müssen, ist zu verstehen, was Git unter „HEAD“ versteht - es ist dort, wo Sie sind, nicht dort, wo sich ein bestimmter Zweig befindet. Und wenn Ihr Standort nicht mit dem eines Zweigs identisch ist, haben Sie keine andere Wahl, als einen abgetrennten KOPF zu verwenden.

(Vielleicht schauen Sie auch in GitHub, gitk oder gitweb nach, um den Commit-Verlauf zu durchsuchen, wenn Sie weiterhin entgleist sind.)


1

Die Frage ist etwas vage, aber wenn Sie nur Dateien in Ihrem Arbeitsbaum ändern möchten, können Sie dies einfach tun:

git checkout [commit|branch] -- .

Sie können dann die Änderungen vornehmen und auf Wunsch ein neues Commit erstellen. Das ist manchmal ziemlich nützlich.


0

Ich glaube ich verstehe deine Fragen. Hier ist, was ich gefunden habe, um es zu lösen. und es gibt keine GUI-Lösung dafür, Sie können nur den Befehl verwenden, um es zu lösen, und es ist wirklich einfach.

Schritt 1: Erstellen Sie ein Tag des alten Commits, zu dem Sie zurückkehren möchten.

wie Tag v2.0

Schritt 2: Git Checkout v2.0

Hier ist es, jetzt zeigt Ihr HEAD auf 'v2.0' Commit, aber der Master zeigt immer noch auf das letzte Commit.

C:\Program Files\Git\doc\git\html\git-checkout.html Dieses Dokument kann Ihnen helfen

oder geben Sie git help <check> ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.