Разделите большой репозиторий 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/

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

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

Unutbu
источник
Страница руководства 'git filter-branch' рекомендует создать новый клон перезаписанного репозитория вместо последнего шага, упомянутого выше.
Якуб Наребски
Я попробовал это и получил ошибку, когда пытался удалить папку .git-rewrite в конце.
MikeM
-d <path-on-another-physical-disk> работал у меня и устранил сбои stange 'mv' в --tree-filter.
Vertigo
У вас есть идея, как получить самую первую фиксацию, если она связана с исключенным путем (например 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
Гениально! Единственное найденное мной решение, которое сохраняет все ветки нетронутыми
pheelicks
Странно, для меня он останавливается с git remote rm origin, который всегда кажется , что возвращение 1. Поэтому я заменил &&на ;эту строку.
kynan
Хорошо, $ @ при необходимости работает более чем с двумя каталогами. По окончании звоню git remote add origin $TARGET; git push origin master.
Walter A
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.
Vangorra
источник
1

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

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

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

Это сработало для того, что мне было нужно.

РЕДАКТИРОВАТЬ: Конечно, то же самое было сделано в My12Repo для каталогов A и B. Это дало мне два репозитория с идентичной историей до того момента, когда я удалил ненужные каталоги.

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