Erstellen Sie ein Submodul-Repository aus einem Ordner und behalten Sie dessen Git-Commit-Verlauf bei


111

Ich habe eine Webanwendung, die andere Webanwendungen auf bestimmte Weise untersucht. Es enthält einige Web-Demos in einem demosOrdner und eine der Demos sollte jetzt ein eigenes Repository haben. Ich möchte ein separates Repository für diese Demo-Anwendung erstellen und daraus machenUnterpaket Submodul aus dem Haupt-Repository, ohne den Commit-Verlauf zu verlieren.

Ist es möglich, den Festschreibungsverlauf aus den Dateien im Ordner eines Repositorys beizubehalten, daraus ein Repository zu erstellen und es stattdessen als Submodul zu verwenden?


Ich habe gesucht, wie Verzeichnis 1 von Git-Repository A in Git-Repository B verschoben werden kann. +1 für den Link zum Artikel.
Chetabahana


Ja, das ist in der Tat sehr ähnlich, die Lösungen unterscheiden sich ein wenig, danke, dass Sie dies geteilt haben
GabLeRoux

Antworten:


190

Detaillierte Lösung

In der Anmerkung am Ende dieser Antwort (letzter Absatz) finden Sie eine schnelle Alternative zu Git-Submodulen mit npm;)

In der folgenden Antwort erfahren Sie, wie Sie einen Ordner aus einem Repository extrahieren, ein Git-Repository daraus erstellen und ihn dann als Submodul anstelle eines Ordners einfügen.

Inspiriert von Gerg Bayers Artikel Verschieben von Dateien von einem Git-Repository in ein anderes, Bewahren der Geschichte

Am Anfang haben wir so etwas:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

In den folgenden Schritten werde ich dies someLibals bezeichnen <directory 1>.

Am Ende werden wir so etwas haben:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Erstellen Sie ein neues Git-Repository aus einem Ordner in einem anderen Repository

Schritt 1

Holen Sie sich eine neue Kopie des Repositorys zum Teilen.

git clone <git repository A url>
cd <git repository A directory>

Schritt 2

Der aktuelle Ordner ist das neue Repository. Entfernen Sie daher die aktuelle Fernbedienung.

git remote rm origin

Schritt 3

Extrahieren Sie den Verlauf des gewünschten Ordners und schreiben Sie ihn fest

git filter-branch --subdirectory-filter <directory 1> -- --all

Sie sollten jetzt ein Git-Repository mit den Dateien aus directory 1dem Stammverzeichnis Ihres Repos mit dem gesamten zugehörigen Commit-Verlauf haben.

Schritt 4

Erstellen Sie Ihr Online-Repository und pushen Sie Ihr neues Repository!

git remote add origin <git repository B url>
git push

Möglicherweise müssen Sie den upstreamZweig für Ihren ersten Push festlegen

git push --set-upstream origin master

Sauber <git repository A>(optional, siehe Kommentare)

Wir wollen Spuren (Dateien und begehen Geschichte) löschen von <git repository B>aus <git repository A>so Geschichte für diesen Ordner nur einmal dort ist.

Dies basiert auf dem Entfernen vertraulicher Daten aus Github.

Gehen Sie zu einem neuen Ordner und

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Ersetzen Sie <directory 1>durch den Ordner, den Sie entfernen möchten. -rwird es rekursiv innerhalb des angegebenen Verzeichnisses tun :). Jetzt drücke origin/mastermit--force

git push origin master --force

Boss Stage (siehe Hinweis unten)

Erstellen Sie ein Submodul von <git repository B>in<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Überprüfen Sie, ob alles wie erwartet funktioniert hat und push

git push origin master

Hinweis

Nachdem ich das alles getan hatte, wurde mir in meinem Fall klar, dass es angemessener war, stattdessen npm zu verwenden, um meine eigenen Abhängigkeiten zu verwalten. Wir können Git-URLs und -Versionen angeben, siehe die Git-URLs package.json als Abhängigkeiten .

Wenn Sie dies auf diese Weise tun, muss das Repository, das Sie als Anforderung verwenden möchten, ein npm-Modul sein, sodass es eine package.jsonDatei enthalten muss. Andernfalls wird folgende Fehlermeldung angezeigt : Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (alternative Lösung)

Möglicherweise ist es einfacher, npm zu verwenden und Abhängigkeiten mit Git-URLs zu verwalten :

  • Verschieben Sie den Ordner in ein neues Repository
  • Führen Sie npm initbeide Repositorys aus
  • Führen npm install --save git://github.com/user/project.git#commit-ishSie dort aus, wo Ihre Abhängigkeiten installiert werden sollen

39
Schritt "Clean <Git Repository A>" sollte vermieden werden. Auf diese Weise können Sie ältere Versionen / Commits aus Ihrem Verlauf nicht vollständig wiederherstellen / auschecken. Sie sollten nur den Ordner eingeben und das Submodul hinzufügen. Sie stellen also sicher, dass Sie beim Auschecken älterer Commits über eine voll funktionsfähige Kopie verfügen.
Cybot

Sollten Sie nicht cd someLibvor Schritt 2 tun ? Sie sagen "Der aktuelle Ordner wird das neue Repository sein", aber tatsächlich nicht. Das neue Repository (Submodul) befindet sich in diesem Ordner.
Jago

1
Bestätigung: Ja, es funktioniert für mehr als ein Submodul. Vielen Dank für die ausführliche Antwort. Musste auch nicht npm verwenden.
Breno Inojosa

2
Ich würde Informationen über das hinzufügen, refs/original/...was in Schritt 3 erstellt wird.
Emile Bergeron

6
GitHub schrieb einen Artikel darüber, wie man einen Ordner in ein neues Repository extrahiert
jrobichaud

9

Die Lösung von @GabLeRoux zerquetscht die Zweige und die damit verbundenen Commits.

Eine einfache Möglichkeit, all diese zusätzlichen Zweige und Commits zu klonen und zu behalten:

1 - Stellen Sie sicher, dass Sie diesen Git-Alias ​​haben

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Klonen Sie die Fernbedienung, ziehen Sie alle Zweige, wechseln Sie die Fernbedienung, filtern Sie Ihr Verzeichnis und drücken Sie

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

3

Die Lösung von GabLeRoux funktioniert gut, es sei denn, Sie verwenden git lfsund haben große Dateien unter dem Verzeichnis, das Sie trennen möchten. In diesem Fall bleiben nach Schritt 3 alle großen Dateien Zeigerdateien anstelle von echten Dateien. Ich denke, es liegt wahrscheinlich daran, dass die .gitattributesDatei im Filterverzweigungsprozess entfernt wird.

Als ich das erkenne, finde ich, dass die folgende Lösung für mich funktioniert:

cp .gitattributes .git/info/attributes

Kopieren, mit .gitattributeswelchem ​​git lfs große Dateien in ein .git/Verzeichnis verfolgt werden, um nicht gelöscht zu werden.

Wenn der .gitattributesFilterzweig fertig ist, vergessen Sie nicht, das zurückzusetzen, wenn Sie git lfs weiterhin für das neue Repository verwenden möchten:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
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.