Создайте репозиторий подмодулей из папки и сохраните историю фиксации git

У меня есть веб-приложение, которое исследует другие веб-приложения определенным образом. Он содержит некоторые веб-демонстрации в папке demos, и одна из демонстраций теперь должна иметь собственный репозиторий. Я хотел бы создать отдельный репозиторий для этого демонстрационного приложения и сделать его subpackage submodule из основного репозитория без теряя свою историю фиксации.

Можно ли сохранить историю фиксации из файлов в папке репозитория и создать из нее репозиторий и использовать его как submodule вместо?

Ответ 1

Подробное решение

См. примечание в конце этого ответа (последний абзац) для быстрой альтернативы подмодулям git с использованием npm;)

В следующем ответе вы узнаете, как извлечь папку из репозитория и создать из него репозиторий git, а затем включить его как submodule вместо папки.

Вдохновленный из статьи Герга Байера Перемещение файлов из одного репозитория git в другую, сохранение истории

В начале у нас есть что-то вроде этого:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

В приведенных ниже шагах я назову это someLib как <directory 1>.

В конце у нас будет что-то вроде этого:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Создайте новый репозиторий git из папки в другом репозитории

Шаг 1

Получить новую копию репозитория для разделения.

git clone <git repository A url>
cd <git repository A directory>

Шаг 2

Текущей папкой будет новый репозиторий, поэтому удалите текущий пульт.

git remote rm origin

Шаг 3

Извлечь историю нужной папки и зафиксировать ее

git filter-branch --subdirectory-filter <directory 1> -- --all

Теперь вы должны иметь репозиторий git с файлами из directory 1 в вашем корне репо со всей связанной историей фиксации.

Шаг 4

Создайте свой онлайн-репозиторий и нажмите новый репозиторий!

git remote add origin <git repository B url>
git push

Возможно, вам понадобится установить ветвь upstream для вашего первого нажатия

git push --set-upstream origin master

Очистить <git repository A> (необязательно, см. комментарии)

Мы хотим удалить трассировку (файлы и историю фиксации) из <git repository B> из <git repository A>, так что история для этой папки существует только один раз.

Это основано на Удаление конфиденциальных данных из github.

Перейдите в новую папку и

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Замените <directory 1> на папку, которую вы хотите удалить. -r будет делать это рекурсивно внутри указанного каталога:). Теперь нажмите origin/master с помощью --force

git push origin master --force

Этап Boss (см. примечание ниже)

Создайте submodule из <git repository B> в <git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Убедитесь, что все работает как ожидалось и push

git push origin master

Примечание

Сделав все это, я понял в своем случае, что более целесообразно использовать npm для управления моими зависимостями. Мы можем указать URL-адреса и версии git, см. package.json git URL-адреса в качестве зависимостей.

Если вы сделаете это так, репозиторий, который вы хотите использовать в качестве требования, должен быть модулем npm, поэтому он должен содержать файл package.json, или вы получите эту ошибку: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

TL;DR (альтернативное решение)

Вам может быть проще использовать npm и управлять зависимостями с git URL-адресами:

  • Переместить папку в новый репозиторий
  • запустите npm init внутри обоих репозиториев
  • запустите npm install --save git://github.com/user/project.git#commit-ish, где вы хотите установить ваши зависимости.

Ответ 2

Решение @GabLeRoux выдавливает ветки, а соответствующие коммиты.

Простой способ клонирования и сохранения всех этих дополнительных ветвей и коммитов:

1 - Убедитесь, что у вас есть этот псевдоним git

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - клонируйте пульт, потяните все ветки, измените пульт, отфильтруйте свою директорию, нажмите

git clone [email protected]:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin [email protected]:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

Ответ 3

Решение GabLeRoux хорошо работает, если вы используете git lfs и имеете большие файлы под каталогом, который хотите отделить. В этом случае после шага 3 все большие файлы останутся файлами указателей вместо реальных файлов. Вероятно, это связано с тем, что файл .gitattributes удаляется в процессе ветвления фильтра.

Понимая это, я считаю, что для меня работает следующее решение:

cp .gitattributes .git/info/attributes

Копирование .gitattributes, которое git lfs использует для отслеживания больших файлов в директории .git/, чтобы избежать удаления.

Когда ветвь фильтра завершена, не забудьте вернуть .gitattributes, если вы все еще хотите использовать git lfs для нового репозитория:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'