2010-10-11 2 views
73

После успешной конвертации SVN-репозитория в Git у меня теперь есть очень большой репозиторий Git, который я хочу разбить на несколько меньших репозиториев и поддерживать историю.Сплит большой репозиторий Git во многие более мелкие

Так, кто-то может помочь с разбивая репо, которые могли бы выглядеть следующим образом:

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

Into два репозиториев, которые выглядят так:

MyABRepo/ 
    .git 
    DIR_A/ 
    DIR_B/ 

My12Repo/ 
    .git 
    DIR_1/ 
    DIR_2/ 

Я попытался следующие направления в этом предыдущий вопрос, но он не подходит при попытке поместить несколько каталогов в отдельный репо (Detach (move) subdirectory into separate Git repository).

+5

Когда вы довольны ответом, отметьте его как принятый. –

+0

Для тех, кто хочет разделить несколько (вложенных) каталогов на новое репо (вместо того, чтобы искать несколько каталогов, что может быть сложнее для некоторых проектов), этот ответ был полезен для меня: http://stackoverflow.com/a/19957874/164439 – thaddeusmt

ответ

69

Это установит 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-переписывают, вы можете попробовать это:

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, если у вас есть разрешение на запись, а каталог еще не существует.

+0

«git filter-branch» manpage рекомендует создать новый клон переписанного репозитория вместо последнего упомянутого выше шага. –

+0

@Jakub: Спасибо за исправление. – unutbu

+0

Я пробовал это и получил ошибку, когда пытался удалить папку .git-rewrite в конце. – MikeM

8

Вы можете использовать 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' "[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 

Вам нужно будет вручную удалить ненужные ветви каждого репозитория или метки (например, если у вас функция-х-х-AB ветвь, то вы, вероятно, хотите удалить, что из репозитория «12»).

+0

':' не является символом комментария в bash. Вместо этого вы должны использовать '#'. – Daenyth

+3

@Daenyth, ':' - традиционная встроенная команда ([также указана в POSIX] (http://www.opengroup.org/onlinepubs/009695399/utilities/colon.html)). Он включен в * bash *, но это не комментарий. Я специально использовал его, предпочитая '#', потому что не все оболочки принимают '#' в качестве обозревателя комментариев во всех контекстах (например, интерактивный * zsh * без включенной опции INTERACTIVE_COMMENTS). Использование ':' делает весь текст подходящим для вставки в любую интерактивную оболочку, а также для сохранения в файле сценария. –

+0

Блестящий! Единственное решение, которое я нашел, что все ветви оставлены неизменными – pheelicks

0

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

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

+0

как это так? У меня все еще есть история, даже для удаленных файлов. – MikeM

+1

Поскольку ваше требование не было в том, что репо A должно притворяться, что репо B никогда не существовало, я думаю, что это (оставляя запись фиксаций, которые затрагивают только B), является подходящим решением. Лучше дублировать небольшую историю, чем калечить ее. –

3

Проект 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.