Teilen Sie das große Git-Repository in viele kleinere auf

85

Nachdem ich ein SVN-Repository erfolgreich in Git konvertiert habe, habe ich jetzt ein sehr großes Git-Repository, das ich in mehrere kleinere Repositorys aufteilen und den Verlauf pflegen möchte.

Kann jemand helfen, ein Repo aufzubrechen, das so aussehen könnte:

MyHugeRepo/
   .git/
   DIR_A/
   DIR_B/
   DIR_1/
   DIR_2/

In zwei Repositories, die so aussehen:

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

Ich habe versucht, den Anweisungen in dieser vorherigen Frage zu folgen, aber es passt nicht wirklich, wenn versucht wird, mehrere Verzeichnisse in ein separates Repo zu stellen ( Unterverzeichnis "Trennen (Verschieben)" in ein separates Git-Repository ).

MikeM
quelle
10
Wenn Sie mit einer Antwort zufrieden sind, markieren Sie sie bitte als akzeptiert.
Ben Fowler
Für alle, die mehrere (verschachtelte) Verzeichnisse in ein neues Repo aufteilen möchten (anstatt mehrere Verzeichnisse zu entfernen, was bei einigen Projekten möglicherweise schwieriger ist), war diese Antwort hilfreich für mich: stackoverflow.com/a/19957874/164439
thaddeusmt

Antworten:

79

Dadurch wird MyABRepo eingerichtet. Sie können My12Repo natürlich ähnlich machen.

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 

Ein Verweis auf .git / refs / original / refs / Heads / Master bleibt erhalten. Sie können das entfernen mit:

cd ..
git clone MyABRepo.tmp MyABRepo

Wenn alles gut gegangen ist, können Sie MyABRepo.tmp entfernen.


Wenn Sie aus irgendeinem Grund eine Fehlermeldung bezüglich .git-rewrite erhalten, können Sie Folgendes versuchen:

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch -d /tmp/git-rewrite.tmp --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 
cd ..
git clone MyABRepo.tmp MyABRepo

Dadurch wird /tmp/git-rewrite.tmp anstelle von als temporäres Verzeichnis erstellt und verwendet .git-rewrite. Natürlich können Sie jeden gewünschten Pfad anstelle ersetzen /tmp/git-rewrite.tmp, solange Sie über Schreibberechtigung verfügen und das Verzeichnis noch nicht vorhanden ist.

unutbu
quelle
Die Manpage 'git filter-branch' empfiehlt, anstelle des oben genannten letzten Schritts einen neuen Klon des neu geschriebenen Repositorys zu erstellen.
Jakub Narębski
Ich habe dies versucht und beim Versuch, den Ordner .git-rewrite am Ende zu löschen, eine Fehlermeldung erhalten.
MikeM
-d <Pfad-on-another-physical-disk> hat für mich funktioniert und seltsame 'mv'-Fehler innerhalb von --tree-filter beseitigt.
Schwindel
Haben Sie eine Idee, wie Sie das allererste Commit ausführen können, wenn es sich um einen ausgeschlossenen Pfad handelt (wie DIR_Azum Beispiel)?
Bitmaske
1
Ich erkannte nicht die vollständigen Auswirkungen von filter-branch. Für diejenigen, die es nicht wissen, wird der Verlauf neu geschrieben. Wenn Sie also vorhaben, das Repo zu pushen, nachdem Sie dies getan haben, sind die Commit-Hashes jetzt anders und es funktioniert nicht.
thaddeusmt
10

Sie könnten verwenden git filter-branch --index-filtermit git rm --cachedden unerwünschten Verzeichnisse von Klonen / Kopien Ihrer Original - Repository zu löschen.

Beispielsweise:

trim_repo() { : trim_repo src dst dir-to-trim-out...
  : uses printf %q: needs bash, zsh, or maybe ksh
  git clone "$1" "$2" &&
  (
    cd "$2" &&
    shift 2 &&

    : mirror original branches &&
    git checkout HEAD~0 2>/dev/null &&
    d=$(printf ' %q' "$@") &&
    git for-each-ref --shell --format='
      o=%(refname:short) b=${o#origin/} &&
      if test -n "$b" && test "$b" != HEAD; then 
        git branch --force --no-track "$b" "$o"
      fi
    ' refs/remotes/origin/ | sh -e &&
    git checkout - &&
    git remote rm origin &&

    : do the filtering &&
    git filter-branch \
      --index-filter 'git rm --ignore-unmatch --cached -r -- '"$d" \
      --tag-name-filter cat \
      --prune-empty \
      -- --all
  )
}
trim_repo MyHugeRepo MyABRepo DIR_1 DIR_2
trim_repo MyHugeRepo My12Repo DIR_A DIR_B

Sie müssen die nicht benötigten Zweige oder Tags jedes Repositorys manuell löschen (z. B. wenn Sie einen Feature-x-for-AB- Zweig hatten, möchten Sie diesen wahrscheinlich aus dem Repository „12“ löschen).

Chris Johnsen
quelle
1
:ist kein Kommentarzeichen in bash. Sie sollten #stattdessen verwenden.
Daenyth
4
@Daenyth :ist ein traditioneller integrierter Befehl ( auch in POSIX angegeben ). Es ist in Bash enthalten , aber es ist kein Kommentar. Ich habe es speziell bevorzugt verwendet, #weil nicht alle Shells #in allen Kontexten als Kommentareinführer dienen (z. B. interaktives zsh ohne aktivierte Option INTERACTIVE_COMMENTS). Durch :die Verwendung eignet sich der gesamte Text zum Einfügen in eine beliebige interaktive Shell sowie zum Speichern in einer Skriptdatei.
Chris Johnsen
1
Brillant! Einzige Lösung, die ich gefunden habe, die alle Zweige intakt hält
Pheelicks
Seltsam, für mich hört es mit auf git remote rm origin, was immer 1 zurückzugeben scheint. Daher habe ich das &&durch ;für diese Zeile ersetzt.
Kynan
Schön, dass $ @ bei Bedarf für mehr als zwei Verzeichnisse funktioniert. Wenn ich fertig bin, rufe ich an git remote add origin $TARGET; git push origin master.
Walter A
6

Das Projekt git_split ist ein einfaches Skript, das genau das tut, wonach Sie suchen. https://github.com/vangorra/git_split

Verwandeln Sie Git-Verzeichnisse in eigene Repositorys an ihrem eigenen Standort. Kein Teilbaum lustiges Geschäft. Dieses Skript übernimmt ein vorhandenes Verzeichnis in Ihrem Git-Repository und verwandelt dieses Verzeichnis in ein eigenständiges Repository. Unterwegs wird der gesamte Änderungsverlauf für das von Ihnen angegebene Verzeichnis kopiert.

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.
vangorra
quelle
1

Vielen Dank für Ihre Antworten, aber am Ende habe ich das Repository nur zweimal kopiert und dann die Dateien gelöscht, die ich nicht von jedem wollte. Ich werde den Filterzweig zu einem späteren Zeitpunkt verwenden, um alle Commits für die gelöschten Dateien zu entfernen, da sie bereits an anderer Stelle versioniert sind.

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

cd MyABRepo/
rm -Rf DIR_1/ DIR_2/
git add -A
git commit -a

Dies funktionierte für das, was ich brauchte.

EDIT: Natürlich wurde das gleiche im My12Repo gegen das A- und B-Verzeichnis gemacht. Dies gab mir zwei Repos mit identischem Verlauf bis zu dem Punkt, an dem ich die unerwünschten Verzeichnisse löschte.

MikeM
quelle
1
Dadurch wird der Commit-Verlauf nicht beibehalten.
Daenyth
Wie das? Ich habe immer noch den gesamten Verlauf, auch für die gelöschten Dateien.
MikeM
1
Da Ihre Anforderung nicht war, dass Repo A so tun muss, als ob Repo B nie existiert hätte, halte ich dies (eine Aufzeichnung von Commits, die nur B betrafen) für eine angemessene Lösung. Es ist besser, ein wenig Geschichte zu duplizieren, als sie zu zerfleischen.
Steve Clay