2010-10-11 10 views
73

Po pomyślnej konwersji repozytorium SVN na Git, mam teraz bardzo duże repozytorium Git, które chcę rozbić na wiele mniejszych repozytoriów i zachować historię.Podziel duże repozytorium Git na wiele mniejszych

Więc może ktoś pomóc w zerwaniu repo, które mogą wyglądać następująco:

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

na dwa repozytoria, które wyglądają tak:

MyABRepo/ 
    .git 
    DIR_A/ 
    DIR_B/ 

My12Repo/ 
    .git 
    DIR_1/ 
    DIR_2/ 

Próbowałem po kierunkach w tym poprzednie pytanie, ale tak naprawdę nie pasuje, gdy próbuje się umieścić wiele katalogów w osobnym repo (Detach (move) subdirectory into separate Git repository).

+5

Po uzyskaniu odpowiedzi należy ją oznaczyć jako zaakceptowaną. –

+0

Dla każdego, kto chce podzielić wiele katalogów (zagnieżdżonych) na nowe repozytorium (zamiast szukać kilku katalogów, co może być trudniejsze w niektórych projektach), ta odpowiedź była dla mnie pomocna: http://stackoverflow.com/a/19957874/164439 – thaddeusmt

Odpowiedz

69

Spowoduje to ustawienie MyABRepo; podobnie można zrobić My12Repo.

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 

Odniesienie do .git/refs/original/refs/heads/master pozostaje. Możesz usunąć to za pomocą:

cd .. 
git clone MyABRepo.tmp MyABRepo 

Jeśli wszystko poszło dobrze, możesz usunąć plik MyABRepo.tmp.


Jeśli z jakiegoś powodu się błąd dotyczący .git-rewrite, można spróbować to:

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 

To będzie tworzyć i wykorzystywać /tmp/git-rewrite.tmp jako tymczasowego katalogu zamiast .git-rewrite. Oczywiście, możesz zastąpić dowolną ścieżkę zamiast /tmp/git-rewrite.tmp, o ile masz uprawnienia do zapisu, a katalog jeszcze nie istnieje.

+0

'git filter-branch' manpage zaleca utworzenie nowego klonu przepisanego repozytorium zamiast ostatniego kroku wspomnianego powyżej. –

+0

@Jakub: Dzięki za poprawkę. – unutbu

+0

Próbowałem tego i dostałem błąd, gdy próbował usunąć folder .git-przepisać na końcu. – MikeM

8

Można użyć git filter-branch --index-filter z git rm --cached, aby usunąć niechciane katalogi z klonów/kopii oryginalnego repozytorium.

Na przykład:

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' "[email protected]") && 
    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 

Będziesz musiał ręcznie usunąć niepotrzebne gałęzie każdego repozytorium lub tagi (np jeśli miał cecha-x-for-AB oddział, to prawdopodobnie chcesz usunąć, że z repozytorium "12").

+0

':' nie jest znakiem komentarza w bashu. Zamiast tego powinieneś użyć '# '. – Daenyth

+3

@Denekt, ':' jest tradycyjnym wbudowanym poleceniem ([również określanym w POSIX] (http://www.opengroup.org/onlinepubs/009695399/utilities/colon.html)). Jest zawarty w * bash *, ale nie jest komentarzem. W szczególności użyłem go zamiast "#", ponieważ nie wszystkie powłoki przyjmują '# 'jako wprowadzający komentarze we wszystkich kontekstach (np. Interaktywny * zsh * bez włączonej opcji INTERACTIVE_COMMENTS). Użycie ':' sprawia, że ​​cały tekst nadaje się do wklejenia do dowolnej powłoki interaktywnej, jak również do zapisywania w pliku skryptu. –

+0

Genialny! Jedyne rozwiązanie, które znalazłem, które utrzymuje wszystkie gałęzie w stanie nienaruszonym – pheelicks

0

Dziękuję za odpowiedzi, ale skończyło się po prostu skopiowanie repozytorium dwa razy, a następnie usunięcie plików, których nie chciał od każdego. Zamierzam użyć gałęzi filtra w późniejszym terminie, aby usunąć wszystkie zatwierdzenia dla usuniętych plików, ponieważ są one już kontrolowane w innych wersjach.

cp -R MyHugeRepo MyABRepo 
cp -R MyHugeRepo My12Repo 

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

To działało na to, czego potrzebowałem.

EDYCJA: Oczywiście, to samo zrobiono w My12Repo przeciwko katalogowi A i B. Dało mi to dwie transakcje z identyczną historią aż do momentu, w którym usunąłem niechciane katalogi.

+1

Nie zachowuje historii commitów. – Daenyth

+0

jak to zrobić? Nadal mam całą historię, nawet dla usuniętych plików. – MikeM

+1

Ponieważ twoje wymaganie nie było tym repo Musiałem udawać, że repo B nigdy nie istniało, myślę, że to (pozostawiając zapis zatwierdzeń, które dotyczy tylko B) jest właściwym rozwiązaniem. Lepiej powielać trochę historii niż ją rozplątać. –

3

Projekt git_split to prosty skrypt, który spełnia dokładnie to, czego szukasz.https://github.com/vangorra/git_split

Włącz katalogi git do swoich własnych repozytoriów we własnej lokalizacji. Bez podtremy zabawny biznes. Skrypt ten przeniesie istniejący katalog do repozytorium git i przekształci go w niezależne repozytorium. Po drodze skopiuje całą historię zmian dla podanego katalogu.

./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. 
Powiązane problemy