Розділіть велике сховище Git на багато менших


86

Після успішного перетворення сховища SVN у Git, у мене тепер є дуже велике сховище Git, яке я хочу розбити на декілька менших сховищ та вести історію.

Тож, хтось може допомогти у розбитті репо, яке може виглядати так:

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

У два сховища, які виглядають так:

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

Я спробував дотримуватися вказівок у цьому попередньому питанні, але це насправді не підходить при спробі розмістити кілька каталогів в окремому репо ( Від'єднати (перемістити) підкаталог в окреме сховище Git ).


11
Коли ви задоволені відповіддю, позначте її як прийняту.
Бен Фаулер

1
Для тих, хто хоче розділити кілька (вкладених) каталогів у нове репо (замість того, щоб видалити кілька каталогів, що може бути складніше для деяких проектів), ця відповідь була для мене корисною: stackoverflow.com/a/19957874/164439
thaddeusmt

Відповіді:


80

Це налаштує MyABRepo; Ви можете зробити 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 

Посилання на .git / refs / original / refs / heads / master залишається. Ви можете видалити це за допомогою:

cd ..
git clone MyABRepo.tmp MyABRepo

Якщо все пішло добре, ви можете видалити MyABRepo.tmp.


Якщо з якихось причин ви отримуєте помилку щодо .git-rewrite, ви можете спробувати це:

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

Це створить і використає /tmp/git-rewrite.tmp як тимчасовий каталог, а не як .git-rewrite. Звичайно, ви можете замінити замість нього будь-який шлях /tmp/git-rewrite.tmp, якщо у вас є дозвіл на запис, а каталог ще не існує.


'git filter-branch' manpage рекомендує створити новий клон переписаного сховища замість останнього кроку, згаданого вище.
Якуб Нарембський,

Я спробував це і отримав помилку, коли намагався видалити папку .git-rewrite наприкінці.
MikeM

-d <path-on-another-physical-disk> спрацював у мене і усунув великі помилки «mv» у межах --tree-filter.
Запаморочення

У вас є ідея, як отримати найперший коміт, якщо він пов’язаний із виключеним шляхом (як DIR_A, наприклад)?
bitmask

1
Я не усвідомлював повних наслідків filter-branch. Для тих, хто не обізнаний, він переписує історію, тому, якщо ви плануєте натиснути репо після того, як ви це зробите, хеші комітів тепер будуть іншими, і це не буде працювати.
thaddeusmt

10

Ви можете використовувати за git filter-branch --index-filterдопомогою, git rm --cachedщоб видалити небажані каталоги з клонів / копій оригінального сховища.

Наприклад:

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

Вам потрібно буде вручну видалити непотрібні гілки або теги кожного сховища (наприклад, якщо у вас була гілка feature-x-for-AB , то ви, ймовірно, хочете видалити це зі сховища “12”).


1
:не є символом коментаря в bash. Вам слід використовувати #замість цього.
Daenyth,

4
@Daenyth :- це традиційна вбудована команда ( також зазначена в POSIX ). Він включений у bash , але це не коментар. Я спеціально використав його, #оскільки не всі оболонки приймають #як коментар у всіх контекстах (наприклад, інтерактивний zsh без увімкненої опції INTERACTIVE_COMMENTS). Використання :робить весь текст придатним для вставки в будь-яку інтерактивну оболонку, а також для збереження у файлі сценарію.
Кріс Джонсен,

1
Блискуче! Єдине рішення, яке я знайшов, яке зберігає всі гілки цілими
фелікс

Дивно, для мене він зупиняється з git remote rm origin, який завжди здається , що повернення 1. Тому я замінив &&на ;цей рядок.
kynan

Приємно, $ @ працює при необхідності більше ніж на два курси. Після закінчення телефоную git remote add origin $TARGET; git push origin master.
Вальтер,

6

Проект git_split - це простий скрипт, який робить саме те, що ви шукаєте. https://github.com/vangorra/git_split

Перетворіть каталоги git у свої власні сховища у своєму власному розташуванні. Жодного піддерева смішного бізнесу. Цей сценарій візьме існуючий каталог у вашому сховищі git і перетворить цей каталог на самостійне сховище. Попутно він копіюватиме всю історію змін для вказаного вами каталогу.

./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

Дякую за відповіді, але в підсумку я просто двічі скопіював сховище, а потім видалив файли, яких не хотів із кожного. Я збираюся використовувати гілку фільтра пізніше, щоб видалити всі коміти для видалених файлів, оскільки вони вже контролюються версіями в іншому місці.

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

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

Це спрацювало на те, що мені потрібно.

EDIT: Звичайно, те саме було зроблено в My12Repo щодо каталогів A і B. Це дало мені два репозиторії з однаковою історією аж до того моменту, коли я видалив небажані каталоги.


1
Це не зберігає історію комітів.
Daenyth,

як так? У мене все ще є вся історія, навіть для видалених файлів.
MikeM

1
Оскільки вашою вимогою було не те, що репо А повинно робити вигляд, що репо Б ніколи не існувало, я думаю, що це (залишити запис комітів, які торкалися лише Б) є відповідним рішенням. Краще продублювати трохи історії, ніж її спотворювати.
Steve Clay
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.