Podziel duże repozytorium Git na wiele mniejszych


86

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

Czy ktoś może więc pomóc w rozbiciu repozytorium, które może wyglądać tak:

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

Do dwóch repozytoriów, które wyglądają tak:

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

Próbowałem postępować zgodnie ze wskazówkami w tym poprzednim pytaniu, ale tak naprawdę nie pasuje, gdy próbuję umieścić wiele katalogów w oddzielnym repozytorium ( podkatalog Odłącz (przenieś) do oddzielnego repozytorium Git ).


11
Jeśli odpowiedź Ci odpowiada, oznacz ją jako zaakceptowaną.
Ben Fowler,

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

Odpowiedzi:


80

Spowoduje to skonfigurowanie MyABRepo; oczywiście możesz zrobić My12Repo podobnie.

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 

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

cd ..
git clone MyABRepo.tmp MyABRepo

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


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

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

Spowoduje to utworzenie i użycie /tmp/git-rewrite.tmp jako katalogu tymczasowego zamiast .git-rewrite. Oczywiście zamiast tego możesz zastąpić dowolną ścieżkę /tmp/git-rewrite.tmp, o ile masz uprawnienia do zapisu, a katalog jeszcze nie istnieje.


Strona podręcznika „git filter-branch” zaleca utworzenie nowego klonu przepisanego repozytorium zamiast ostatniego kroku wspomnianego powyżej.
Jakub Narębski

Próbowałem tego i otrzymałem błąd podczas próby usunięcia folderu .git-rewrite na końcu.
MikeM

-d <path-on-another-physical-disk> zadziałało dla mnie i wyeliminowało dziwne błędy „mv” w ramach --tree-filter.
Vertigo

Czy masz pomysł, jak uzyskać pierwsze zatwierdzenie, jeśli jest to związane z wykluczoną ścieżką ( DIR_Ana przykład)?
maska ​​bitów

1
Nie zdawałem sobie sprawy z pełnych konsekwencji filter-branch. Dla tych, którzy nie są tego świadomi, ponownie zapisuje historię, więc jeśli planujesz wypchnąć repozytorium po wykonaniu tej czynności, skróty zatwierdzania będą teraz inne i nie będą działać.
thaddeusmt

10

Możesz użyć git filter-branch --index-filterz, git rm --cachedaby 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' "$@") &&
    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 lub tagi każdego repozytorium (np. Jeśli miałeś gałąź feature-x-for-AB , prawdopodobnie chcesz ją usunąć z repozytorium „12”).


1
:nie jest znakiem komentarza w bash. Zamiast tego powinieneś użyć #.
Daenyth

4
@Daenyth, :to tradycyjne polecenie wbudowane ( również określone w POSIX ). Jest zawarty w bash , ale nie jest to komentarz. Użyłem go specjalnie zamiast tego, #ponieważ nie wszystkie powłoki przyjmują #jako wprowadzające 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 interaktywnej powłoki, a także do zapisania w pliku skryptu.
Chris Johnsen,

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

Dziwne, dla mnie kończy się na git remote rm origin, co zawsze wydaje się zwracać 1. Dlatego zastąpiłem &&przez ;dla tej linii.
kynan,

Świetnie, $ @ działa w razie potrzeby dla więcej niż dwóch katalogów. Po zakończeniu dzwonię git remote add origin $TARGET; git push origin master.
Walter A,

6

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

Zamień katalogi git w ich własne repozytoria we własnej lokalizacji. Brak zabawnego biznesu w poddrzewie. Ten skrypt weźmie istniejący katalog w repozytorium git i zamieni ten katalog 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.


1

Dziękuję za odpowiedzi, ale skończyło się na dwukrotnym skopiowaniu repozytorium, a następnie usunięciu z każdego plików, których nie chciałem. Zamierzam użyć gałęzi filter w późniejszym terminie, aby usunąć wszystkie zatwierdzenia dla usuniętych plików, ponieważ są one już kontrolowane w innym miejscu.

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 zostało zrobione w My12Repo w stosunku do katalogu A i B. Dało mi to dwa repozytoria z identyczną historią do momentu, w którym usunąłem niechciane katalogi.


1
Nie zachowuje to historii zatwierdzeń.
Daenyth

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

1
Ponieważ twoim wymaganiem nie było to, że repozytorium A musi udawać, że repozytorium B nigdy nie istniało, myślę, że to (pozostawiając zapis zatwierdzeń, które dotyczyły tylko B) jest odpowiednim rozwiązaniem. Lepiej powielić trochę historii, niż ją wymieszać.
Steve Clay
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.