Warum kann ich diesen aktuellen Git-Teilbaum nicht verschieben?

87

Ich verwende den Git-Teilbaum mit einigen Projekten, an denen ich arbeite, um einen Basiscode zwischen ihnen zu teilen. Der Basiscode wird häufig aktualisiert, und die Upgrades können in jedem der Projekte durchgeführt werden, wobei alle letztendlich aktualisiert werden.

Ich bin auf ein Problem gestoßen, bei dem git meldet, dass mein Teilbaum auf dem neuesten Stand ist, aber das Drücken wird abgelehnt. Beispielsweise:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

Wenn ich drücke, sollte ich eine Nachricht erhalten, dass es nichts zu pushen gibt ... Richtig? RICHTIG? :(

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Was könnte der Grund dafür sein? Warum scheitert das Schieben?

mateusz
quelle
warum tust du nicht was git dir sagt: git pull?
AlexWien
8
Git Pull hat nichts mit meiner Frage zu tun. Aber ja, Git Pull gibt mir eine aktuelle Nachricht, da das Origin-Repo nichts Neues enthält.
Mateusz
1
Es hört sich so an, als ob sich der Zweig, in dem Sie sich befinden, hinter dem Master-HEAD befindet - zum Beispiel in einer Historie von ... -> A -> B -> C, in dem sich der Remote-Master-HEAD in C befindet, Ihr aktueller Zweig jedoch in A. (oder ein Nachkomme von A, der nicht mit C verbunden ist). Ich würde mir sicher meine Git-Geschichte ansehen.
Mark Leighton Fisher
Ich habe das gleiche Problem. Ich habe verwendet --rejoin, um Teilbaum-Pushs zu beschleunigen. Wenn ich den Teilbaum-Push manuell durchlaufe subtree split --rejoinund laufe, hat der geteilte Teilbaumzweig nur den Verlauf bis zur letzten Wiederverbindung, wenn er normalerweise den gesamten Teilbaumverlauf enthält. Für mich ist dies die unmittelbare Ursache für Fehler beim nicht schnellen Vorlauf. Ich bin mir immer noch nicht sicher, warum die Teilbaumaufteilung einen abgeschnittenen Verlauf generiert.
Hurrymaplelad

Antworten:

97

Ich habe die Antwort auf diesen Blog-Kommentar https://coderwall.com/p/ssxp5q gefunden

Wenn Sie beim Drücken auf das Problem "Aktualisierungen wurden abgelehnt, weil die Spitze Ihres aktuellen Zweigs zurückliegt. Führen Sie die Remote-Änderungen (z. B." Git Pull ") zusammen) stoßen (aus welchen Gründen auch immer, insbesondere beim Git-Verlauf) Dann müssen Sie Git-Befehle verschachteln, damit Sie einen Push auf Heroku erzwingen können. zB anhand des obigen Beispiels:

git push heroku `git subtree split --prefix pythonapp master`:master --force
Eric Woodruff
quelle
2
Hat mir geholfen! Vielen Dank.
Michael Forrest
8
Wie führe ich diesen Befehl unter Windows aus? Ich error: unknown option 'prefix'
bekomme
3
Erstaunlich, das hat mir gerade den Tag gerettet :)
bpipat
2
Richtig, aber wahrscheinlich ist die meiste Verwendung von Teilbaum auf diese Weise ein One-Way-Push für Heroku. Im Allgemeinen wird Heroku nicht als defacto git repo angesehen, aus dem mehrere Benutzer schieben und ziehen können.
Eric Woodruff
11
Es fiel mir schwer, herauszufinden, welche Beziehung herokuund welche pythonappAntwort ich habe . Übersetzung aus der Heroku-spezifischen Sprache:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
Aednichols
37

Unter Windows funktioniert der verschachtelte Befehl nicht:

git push heroku `git subtree split --prefix pythonapp master`:master --force

Sie können das verschachtelte Bit zuerst ausführen:

git subtree split --prefix pythonapp master

Dies gibt (nach vielen Zahlen) einen Token zurück, z

157a66d050d7a6188f243243264c765f18bc85fb956

Verwenden Sie dies im enthaltenen Befehl, z. B.:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
Chris Jordan
quelle
Dies ist eine viel sauberere Lösung. Funktioniert bei mir!
Unendlichkeit
Wenn ich git subtree split --prefix subtreedirectoryname master ausführe, erhalte ich ein schwerwiegendes mehrdeutiges Argument 'master': unbekannte Revision oder Pfad nicht im Arbeitsbaum
Demodave
Dies ist eine großartige Lösung, die ich immer wieder bekommen habe und die git subtreevorher kein Befehl war. Nachdem Sie Ihre Lösung unter Verwendung war ich in der Lage , einen Ordner zu Heroku statt all meine Repo zu implementieren
pakman198
Tolle Lösung und Zeitersparnis
Nisharg Shah
29

Verwenden Sie die --ontoFlagge:

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[BEARBEITEN: Leitet leider subtree pushnicht --ontoan den Basiswert weiter split, daher muss die Operation in zwei Befehlen ausgeführt werden! Wenn das erledigt ist, sehe ich, dass meine Befehle mit denen in einer der anderen Antworten identisch sind, aber die Erklärung ist anders, also lasse ich es trotzdem hier.]

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

Oder wenn Sie keine Bash verwenden:

git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master

Ich habe Stunden damit verbracht, die Quelle des Git-Teilbaums zu durchsuchen, um dies herauszufinden, also hoffe ich, dass Sie es zu schätzen wissen;)

subtree pushbeginnt mit der Ausführung subtree split, die Ihren Commit-Verlauf in ein Format umschreibt, das zum Pushen bereit sein sollte. Die Art und Weise, wie dies geschieht, ist, dass es public/shared/die Vorderseite jedes Pfads entfernt, der es hat, und alle Informationen über Dateien entfernt, die es nicht haben. Das bedeutet, dass alle Commits des Upstream-Sub-Repositorys ignoriert werden, selbst wenn Sie nicht gequetscht ziehen, da sie die Dateien nach ihren bloßen Pfaden benennen. (Commits, die keine Dateien unter berührenpublic/shared/oder Merge-Commits, die mit einem übergeordneten Element identisch sind, werden ebenfalls reduziert. [BEARBEITEN: Außerdem habe ich seitdem eine Squash-Erkennung gefunden. Jetzt denke ich, dass dies nur dann der Fall ist, wenn Sie nicht gequetscht gezogen haben. Dann gelingt es dem in einer weiteren Antwort beschriebenen vereinfachten Zusammenführen des Zusammenführungs-Commits, den nicht gequetschten Pfad und auszuwählen Verwerfen Sie den gequetschten Pfad.]) Das Ergebnis ist, dass das Material, das versucht zu pushen, letztendlich alle Arbeiten enthält, die von jemandem an das aktuelle Host-Repository übergeben wurden, von dem Sie pushen, aber keine Mitarbeiter, die direkt an das Sub-Repository oder über einen anderen Host gebunden sind Repository.

Wenn Sie jedoch verwenden --onto, werden alle Upstream-Commits als OK aufgezeichnet, um wörtlich verwendet zu werden. Wenn der Umschreibungsprozess sie als eines der übergeordneten Elemente einer Zusammenführung ansieht, die neu geschrieben werden soll, werden sie beibehalten, anstatt zu versuchen, sie neu zu schreiben auf die übliche Weise.

entheh
quelle
2
Ich empfehle diese Antwort als die, die mir schon zweimal geholfen hat. Und Beschreibung war das, was ich brauchte, um zu verstehen, wie das Teilen und Schieben an den richtigen Ort funktioniert. Danke dir.
Kamil Wozniak
2
Was ist "Projekt geteilt"?
Colin D
1
@ColinD "project-shared" ist der Name der Fernbedienung. Dies ist ein Name, den Sie bei der Konfiguration des Ortes gewählt haben, von dem abgerufen und an den gesendet werden soll. Sie können eine Liste der Fernbedienungen mit erhalten git remote -v.
Entheh
@entheh die Stunden, die Sie in diesem verbracht haben, haben jsut meinen Tag gemacht. Vielen Dank!
Juni
1
Ich habe festgestellt, dass dieser Fehler immer dann auftritt, wenn Ihr Unterrepository ein Verzeichnis hat, dessen Name mit dem Präfix identisch ist, z. B. wenn Sie das Unterrepository als "libxxx" zum Hauptrepository hinzufügen und Ihr Unterrepository selbst auch ein Unterverzeichnis mit dem Namen "libxxx" hat ". In diesem Fall behandelt git Commits im Unter-Repository fälschlicherweise als Commits im Haupt-Repository und versucht, sie aufzuteilen. Die --ontoOption hilft git dabei, Commits zu identifizieren, die sich bereits im Sub-Repository befinden.
Cosyn
13

Bei einer App vom Typ "GitHub-Seiten", bei der Sie einen Teilbaum "dist" für einen Gh-Pages-Zweig bereitstellen, sieht die Lösung möglicherweise so aus

git push origin `git subtree split --prefix dist master`:gh-pages --force

Ich erwähne dies, da es etwas anders aussieht als die oben angegebenen Heroku-Beispiele. Sie können sehen, dass mein Ordner "dist" im Hauptzweig meines Repos vorhanden ist, und dann schiebe ich ihn als Teilbaum in den Zweig gh-pages, der sich ebenfalls im Ursprung befindet.

Colin D.
quelle
3

Ich bin auch schon einmal auf dieses Problem gestoßen, und hier ist, wie ich es gelöst habe.

Was ich herausfand, war, dass ich einen Zweig hatte, der nicht mit dem lokalen Hauptzweig verbunden war. Dieser Zweig existiert und hängt nur in der Leere. In Ihrem Fall heißt es wahrscheinlich project-shared. Angenommen, dies ist der Fall, und wenn Sie git brancheine lokale project-sharedVerzweigung sehen, können Sie neue Commits an Ihre vorhandene Verzweigung anhängen , project-sharedindem Sie Folgendes ausführen :

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

Ich habe verstanden, dass git subtreeich anfange, einen neuen Zweig zu erstellen --onto, in diesem Fall den lokalen public-sharedZweig. Dann bedeutet der Zweig, einen Zweig zu erstellen, der nur den alten public-sharedZweig ersetzt.

Dadurch bleiben alle vorherigen SHA der public-sharedNiederlassung erhalten. Schließlich können Sie eine

git push project-shared project-shared:master

Angenommen, Sie haben auch eine project-sharedFernbedienung. Dadurch wird das lokale Hängen im leeren project-shared Zweig zum masterZweig der Fernbedienung verschoben project-shared.

snw
quelle
3

Dies liegt an der Einschränkung des ursprünglichen Algorithmus. Bei der Verarbeitung von Merge-Commits verwendet der ursprüngliche Algorithmus ein vereinfachtes Kriterium zum Abschneiden nicht verwandter Eltern. Insbesondere wird geprüft, ob es ein übergeordnetes Element gibt, das denselben Baum hat. Wenn ein solches übergeordnetes Element gefunden wird, wird das Zusammenführungs-Commit reduziert und stattdessen das übergeordnete Commit verwendet, vorausgesetzt, andere Eltern haben Änderungen, die nicht mit dem Unterbaum zusammenhängen. In einigen Fällen würde dies dazu führen, dass Teile des Verlaufs gelöscht werden, wodurch sich tatsächlich Änderungen am Unterbaum ergeben. Insbesondere würde es Sequenzen von Commits löschen, die einen Unterbaum berühren würden, aber zu demselben Unterbaumwert führen würden.

Sehen wir uns ein Beispiel an (das Sie leicht reproduzieren können), um besser zu verstehen, wie dies funktioniert. Betrachten Sie den folgenden Verlauf (das Zeilenformat lautet: commit [tree] subject):

% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
*   E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
*   A [w] add dir/file1.txt

In diesem Beispiel teilen wir uns auf dir. Commits Dund Ehaben den gleichen Baum z, weil wir Commit haben C, das Commit rückgängig macht B, also B-Ctut die Sequenz nichts dir, obwohl sie Änderungen daran hat.

Lassen Sie uns jetzt teilen. Zuerst teilen wir uns auf Commit C.

% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt

Als nächstes teilen wir uns auf Commit E.

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

Ja, wir haben zwei Commits verloren. Dies führt zu dem Fehler, wenn versucht wird, den zweiten Split zu verschieben, da diese beiden Commits nicht vorhanden sind, die bereits im Ursprung enthalten sind.

Normalerweise können Sie diesen Fehler mithilfe von tolerieren push --force, da verworfene Commits im Allgemeinen keine kritischen Informationen enthalten. Langfristig muss der Fehler behoben werden, damit der Split-Verlauf tatsächlich alle Commits enthält, die sich direrwartungsgemäß berühren . Ich würde erwarten, dass das Update eine eingehendere Analyse der übergeordneten Commits für versteckte Abhängigkeiten enthält.

Als Referenz ist hier der Teil des Originalcodes, der für das Verhalten verantwortlich ist.

copy_or_skip()
  ...
  for parent in $newparents; do
      ptree=$(toptree_for_commit $parent) || exit $?
      [ -z "$ptree" ] && continue
      if [ "$ptree" = "$tree" ]; then
          # an identical parent could be used in place of this rev.
          identical="$parent"
      else
          nonidentical="$parent"
      fi
  ...
  if [ -n "$identical" ]; then
      echo $identical
  else
      copy_commit $rev $tree "$p" || exit $?
  fi
klimkin
quelle
Ist der Fehler ab dem 09.05.2020 (Sa) behoben? @klimkin
halbmond halb
Der Fehler ist behoben. Siehe: Contrib / Subtree: Fix "Subtree Split" Skipped-Merge-Fehler behoben. github.com/git/git/commit/…
halbmondhalb
0

Die Antwort von Eric Woodruff hat mir nicht geholfen, aber die folgende hat geholfen:

Normalerweise ziehe ich einen Teilbaum mit der Option "--squash". Es scheint, dass dies die Versöhnung erschwert hat, also musste ich einen Teilbaum ziehen, ohne diesmal zu quetschen, einige Konflikte lösen und dann pushen.

Ich muss hinzufügen, dass der gequetschte Zug keine Konflikte enthüllte, sagte mir, dass alles in Ordnung war.

Zsolt Szatmari
quelle
0

Eine andere [einfachere] Lösung besteht darin, den Kopf der Fernbedienung durch ein weiteres Commit voranzutreiben, wenn Sie können. Nachdem Sie diesen erweiterten Kopf in den lokalen Teilbaum gezogen haben, können Sie ihn erneut verschieben.

9swampy
quelle
1
Genau so habe ich das gleiche Problem mit dem Remote-Repository gelöst, das nicht mit dem lokalen Teilbaum synchronisiert ist.
Aidas Bendoraitis
0

Sie können Push-Local-Änderungen für das Remote-Teilbaum-Repo erzwingen

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
FunkyKat
quelle
0

Eine schnelle PowerShell für Windows-Benutzer basierend auf der Lösung von Chris Jordan

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
Rtype
quelle
0

Das habe ich also geschrieben, basierend auf dem, was @entheh gesagt hat.

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause
Demodave
quelle
0

Hatte auch dieses Problem. Folgendes habe ich getan, basierend auf den Top-Antworten:

Gegeben:

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Geben Sie Folgendes ein:

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

Beachten Sie, dass das Token, das von 'git subtree push' ausgegeben wird, in 'git push' verwendet wird.

Amos
quelle