Как я могу переместить отдельный каталог из репозитория git в новый репозиторий, сохранив при этом историю?

110

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

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

посчитал
источник
2
Добавлен тег git-submodules, так как он очень полезен для преобразования части репо в подмодуль.
idbrii 01
Возможный дубликат поддерева экспорта в Git с историей
пользователь

Ответы:

99

Вы можете использовать, git filter-branchчтобы переписать историю проекта. Из документации:

Чтобы переписать репозиторий, чтобы он выглядел так, как будто foodir / был его корнем проекта, и отбросить всю остальную историю:

git filter-branch --subdirectory-filter foodir -- --all

Сделайте несколько копий своего репо, сделайте это для каждого подкаталога, который хотите разделить, и вы должны получить то, что ищете.

Брайан Кэмпбелл
источник
1
Что, если бы я хотел исключить foodir и удалить всю его историю?
saeedgnu 02
1
Примечание: если вам нужно несколько каталогов, вам нужно будет указать --subdirectory-filterнесколько раз. EG git filter-branch --subdirectory-filter foodir --subdirectory-filter bardirи т. Д. Не --subdirectoryбудут занимать несколько каталогов, но могут быть указаны несколько раз.
EnabrenTane
3
@Adam Если вы хотите сохранить историю foodirв исходном проекте, а не переписывать его историю, git rm -r foodirэтого будет достаточно (это также приведет к удалению копии в вашем рабочем дереве; если вы этого не хотите, используйте --cached). Если вы хотите полностью удалить его из истории (отвечая также на вопрос @ ilius), вам нужно что-то вродеgit filter-branch --index-filter 'git rm -r --cached --ignore-unmatched foodir' -- --all
Брайан Кэмпбелл
2
@ilius Извините, я пропустил ваш вопрос ранее, см. мой ответ на выше, чтобы узнать, как удалить каталог и его историю.
Брайан Кэмпбелл
2
@ilius Ага, это тоже работает. Для большого проекта --index-filterрешение работает быстрее --tree-filter, поскольку ему не нужно фактически извлекать файлы, оно может просто напрямую манипулировать индексом. Однако --tree-filterможет быть немного проще использовать, поскольку вы можете использовать обычные операции файловой системы, а не работать с операциями манипулирования индексами.
Брайан Кэмпбелл
4

Для экспорта папки как нового репозитория вам необходимо:

  1. Чтобы клонировать репозиторий, в котором находится папка, которую вы хотите экспортировать.
  2. Чтобы создать пустой репозиторий на вашем хостинг-провайдере как GitHub, для хранения экспортированной папки.
  3. Откройте папку клонированного репозитория и выполните эту команду:

    git subtree push --prefix=YourFolderNameToExport https://github.com/YourUserName/YourNewCleanRepoName master
    
пользователь
источник
1
git subtreeнедоступен как пакет Cygwin. Если вам это нужно: stackoverflow.com/a/27116828/2484903
Джек Миллер,
2

Суть git в том, что история воплощается в каждой фиксации путем хеширования родительской фиксации. Вы можете «воспроизвести» коммиты (по сути, так работает svn-importer) в новый репозиторий, сохранив только каждый подпроект. Это, однако, разрушило бы смысл хэшей фиксации. Если у вас нет проблем с этим, пусть будет так.

Раньше я просто клонировал его и двигался дальше. Это делает вещи больше, но дисковое пространство дешево; мое время дорого.

Я также не знаю никаких инструментов для создания каталога. Я полагаю, вы могли бы git-log в каталоге найти все коммиты в нем, а затем воспроизвести коммиты с помощью чего-то вроде git-fast-export?

Колин Бернетт
источник
Я поддержал, потому что это не заслуживает отрицательного ответа - я бы точно не стал следовать этому подходу, но вижу, что он пытался
Элвин