fragen stichworte

Migrieren Sie mehrere svn-Repositorys in ein einzelnes Git-Repository

Wir möchten dauerhaft von svn auf git migrieren, um die besseren Funktionen von git in Bezug auf Verzweigung und Zusammenarbeit nutzen zu können.

Unser aktuelles svn-Repository sieht so aus

svnrepo/
   frontend/
      trunk
      branches/
         ng/
         ...
      tags/
         1.x
         ...
   backend/
      trunk
      branches/
         ng/
         ...
      tags/
         1.x
         ...

Das Arbeitslayout sieht so aus, dass wir das Frontend-Projekt auschecken und darin einen Backend-Ordner erstellen und das Backend-Projekt auschecken.

Wir möchten nun zu git wechseln und die Trennung zwischen Frontend und Backend (als separate Projekte) aufgeben, da dies mehr Probleme als Vorteile mit sich bringt. Wir möchten, dass sich beide in einem einzigen Git-Repository befinden.

Ich wollte svn2git für die Konvertierung verwenden. Leider passierte die neueste Entwicklung in einer Branche und nicht im Kofferraum, aber ich denke, dies sollte für svn2git kein Problem sein. Das neue Git-Repository-Layout sollte also so aussehen:

/           => svnrepo/frontend/branches/ng
/backend     => svnrepo/backend/branches/ng

Where => bedeutet "migriert/konvertiert von".

Für die Konvertierung müssen wir nicht alle Tags und Verzweigungen aus dem konvertieren SVN-Repository auf git. Das ist für uns nicht wichtig. Wichtig ist jedoch, dass wir die vollständige Historie aller Commits für alle Dateien im Branches/ng-Verzeichnis haben und zur Verzweigung von Trunk und allen Commits zurückkehren, die zuvor in Trunk stattfanden. Wir möchten, dass all diese Verpflichtungen mit dem erwähnten Layout in einem einzigen Git-Repository liegen. Ist das überhaupt möglich? Und wie machen wir das?

Ich habe bereits mit google und auch in stackoverflow 1, 2 gesucht, konnte jedoch keine exakte Lösung für unser Problem finden.

antworten

Eine Lösung wäre, jedes der Repositories separat mit svn2git oder einfach mit git svn zu generieren (es ist ein schönes kleines Werkzeug, das bereits in git integriert ist) und es dann mit git filter-branch zu verbinden.

  1. Klonen Sie jedes svn-Repository einzeln.
  2. Fügen Sie im Repository, das Sie root sein möchten, die anderen Repositorys als Remotes hinzu, und rufen Sie die Zweige ab, die Sie mit dem Repo zusammenführen möchten (Sie erhalten Warnungen, da die Zweige keine gemeinsame Historie haben; dies wird erwartet).
  3. Führen Sie git filter-branch für diese neuen Zweige aus, und verwenden Sie einen Indexfilter, um ein neues Unterverzeichnis für sie zu erstellen.
  4. Führen Sie die gefilterten Zweige in master (oder in einen beliebigen Zweig) im Root-Repository ein. Die volle Geschichte würde erhalten bleiben.

Der Befehl für Schritt 3 würde ungefähr so ​​aussehen:

git filter-branch --index-filter '
    git ls-files -s |
    perl -pe "s{\t\"?}{$&newsubdir/}" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD

Die Magie, und jedes Mal, wenn ich das tun muss, fühlt es sich ein bisschen wie Magie an, lautet die Aussage von perl. git filter-branch filtert den Index bei jedem Commit und setzt alle Blob-Pfade (d. h. die Dateipfade des Arbeitsbaums ändern) mit 'newsubdir' voran. Möglicherweise müssen Sie herum experimentieren, um die Pfade genau richtig zu machen. Ein paar Lektionen, die man von jemandem gelernt hat, der zuvor gegangen ist:

  • Sichern Sie alles. git filter-branch ist die Geschichte zerstörerisch. Wenn Sie es einmal geändert haben, können Sie es nicht mehr einfach rückgängig machen. Sichern Sie alle von Ihnen verwendeten Repository-Kopien. Nichts ist schlimmer, als eine komplexe Operation zu beenden und festzustellen, dass Sie ein / im Pfad verpasst haben.
  • Schreiben Sie alles. Wenn Sie nicht über ernsthafte Fähigkeiten verfügen; Sie werden das nicht beim ersten Mal richtig machen. Schreiben Sie jeden einzelnen Schritt nach dem Abschluss ab, sodass das erneute Ausführen von Schritten problemlos möglich ist. Wenn Sie eine Woche später feststellen, dass Sie eine Flagge vermasselt haben, können Sie sie in wenigen Augenblicken replizieren.
  • Geben Sie $ 20 für eine Cluster-Recheninstanz in EC2 aus. git filter-branch ist enorm rechenintensiv. Es kann Stunden dauern, bis ein Indexfilter für einen vertieften Verlauf in Ihrer lokalen Umgebung ausgeführt wird, jedoch nur einen Bruchteil dieser Zeit für eine AWS-Cluster-Recheninstanz. Sicher, sie kosten etwas mehr als 2 US-Dollar pro Stunde,, aber Sie brauchen nur ein paar Stunden. Sparen Sie sich den Schmerz und verwenden Sie die Skripts, die Sie auf Hardware geschrieben haben, die die Operation trivial macht. Es kostet den Preis für ein schönes Mittagessen.

Eine der Lösungen besteht darin, beide SVN-Projekt-Repositories in 2 Git-Repositories zu konvertieren und dann ein Git-Repository als Git-Submodul eines anderen hinzuzufügen.

Um Ihr SVN-Repository in Git-Repositories zu konvertieren, können Sie ein beliebiges git-svn-basiertes Skript oder SubGit verwenden. Mit dem neuesten Tool führen Sie einen einzelnen Befehl aus

$ subgit install path/to/svn/repository

Die konvertierten Git-Repositories befinden sich in Pfad/zu/svn/repository/git.

Dann richten Sie einen Zugriff auf beide Git-Repositories ein und fügen eins als Submodul eines anderen hinzu:

$ git clone <frontend_GitURL> frontend
$ git co
$ cd frontend
$ git submodule add -b ng <backend_GitURL> backend

Ich kann nur daran denken, dass dies extreme Hackereien erfordern wird, es sei denn, svn2git (von denen ich kein Experte

Das Problem ist, dass ein Commit von frontend völlig unabhängig von einem Commit in backend ist. Es gibt keine wirkliche Möglichkeit zu sagen, welcher Commit welchem ​​Commit in einem einzelnen Repository zugeordnet wird. Dies lässt uns nur eine echte Option: Die Geschichte wird aus zwei zusammenfließenden Zweigen bestehen, die die Geschichte des ursprünglichen Projekts repräsentieren, und dann, wenn sie zusammengeführt sind, ist der neue Zweig das "bessere Modell".

Ab jetzt nehme ich an, dass Sie frontend im svn-frontend Zweig importiert und backend im svn-backend Zweig importiert haben und beide ihren eigenen Verlauf enthalten.

Das erste Problem besteht darin, svn-backend im backend/ -Verzeichnis zu fixieren:

git checkout svn-backend
git filter-branch --index-filter '
  git ls-files -s |
  perl -pe "s{\\t\\"?}{$&newsubdir/}" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
  mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD

(Siehe diese Dokumentation und die und die Antwort von @Christopher)

Nun, wenn diese nicht irgendwie das gleiche Commit wie eine Base enthalten (unwahrscheinlich, es sei denn, svn2git erzeugt ein vordefiniertes Basis-Commit oder etwas ...), müssen wir eins machen. Es sollte egal sein, welcher Zweig Sie starten.

git symbolic-ref HEAD refs/heads/svn-base
rm .git/index
git clean -dxf

Git kann leere Verzeichnisse nicht verfolgen. Ich habe nie getestet, ob dies für das Stammverzeichnis gilt, aber meine Annahme ist nicht, also erstellen Sie eine leere git ignore file und commit:

touch .gitignore
git add .gitignore
git commit -m "Base for SVN branches"

Lässt die Geschichte umschreiben:

git rebase svn-base svn-frontend
git rebase svn-base svn-backend

Wir sind fast fertig. Lassen Sie uns nun den Masterzweig erstellen. Wenn es bereits existiert:

git update-ref master "$head"

Sonst:

git branch master

Lasst es uns sehen:

git checkout master

Schließlich die Zusammenführung:

git merge svn-backend

Es ist eine gute Idee, die alten Zweige zu markieren und dann zu löschen:

git checkout svn-frontend
git tag svn-frontend
git branch -d svn-frontend
git checkout svn-backend
git tag svn-backend
git branch -d svn-backend
git checkout master
git branch -d svn-base