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

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

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


person GabLeRoux    schedule 01.07.2013    source источник
comment
Я искал, как переместить каталог 1 из репозитория Git A в репозиторий Git B. +1 за ссылку на статью.   -  person Chetabahana    schedule 09.04.2015
comment
Дублировать? stackoverflow.com/ вопросы/12514197/   -  person naught101    schedule 26.11.2015
comment
Да, это действительно очень похоже, решения немного отличаются, спасибо, что поделились этим   -  person GabLeRoux    schedule 26.11.2015


Ответы (3)


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

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

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

На основе статьи Герга Байера Перемещение файлов из из одного репозитория 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

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

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

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

Создайте подмодуль из <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-urls как зависимости.

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

tldr (альтернативное решение)

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

  • Переместить папку в новый репозиторий
  • запустите npm init внутри обоих репозиториев
  • запустите npm install --save git://github.com/user/project.git#commit-ish там, где вы хотите установить свои зависимости
person GabLeRoux    schedule 08.08.2013
comment
Шаг Очистите ‹git-репозиторий A›, которого следует избегать. Делая это, вы не можете полностью восстановить/извлечь старые версии/коммиты из своей истории. Вы должны просто git rm папку и добавить подмодуль. Таким образом, вы гарантируете, что у вас есть полностью рабочая копия при проверке старых коммитов. - person Cybot; 03.02.2014
comment
Разве вы не должны сделать cd someLib до шага 2? Вы говорите, что текущая папка будет новым репозиторием, но на самом деле это не так; новый репозиторий (подмодуль) находится внутри этой папки. - person Jago; 13.10.2015
comment
Не совсем так, так как someLib на самом деле <directory 1>, следующие команды передают <directory 1> по желанию - person GabLeRoux; 13.10.2015
comment
подтверждаю: да, это работает для более чем одного подмодуля. Большое спасибо за подробный ответ. Кроме того, не нужно было использовать npm. - person Breno Inojosa; 20.10.2015
comment
Рад видеть, что это помогло, и да, действительно, извлечение всего в более чем один подмодуль не должно сильно отличаться. npm был скорее личным предложением, так как в то время моей команде было проще его использовать, я извлек историю, но использовал npm вместо подмодулей. - person GabLeRoux; 20.10.2015
comment
Я бы добавил информацию о refs/original/..., созданном на шаге 3. - person Emile Bergeron; 04.08.2016
comment
Может быть проблема с git-lfs, где вы застряли? Любая ошибка? - person GabLeRoux; 11.09.2016
comment
GitHub сделал статью о том, как добиться извлечения папки в новый репозиторий: help.github.com/articles/ - person jrobichaud; 13.02.2018
comment
существует ли простой или распространенный способ обработки внешней папки, если она находится в подпути, а не непосредственно в корне? Вопрос связан с новым репо, но в первую очередь со старым, где сейчас нужно загрузить новый. - person David; 22.10.2020
comment
@David Дэвид, я не уверен, что вы подразумеваете под аутсорсинговой папкой, но приведенные выше решения должны применяться независимо от того, где находятся папки. Просто каждый раз вводите относительный путь к каждому из них. <git repository A directory> может быть каталогом в каталоге. - person GabLeRoux; 22.10.2020
comment
Кстати, этот вопрос касается аналогичной проблемы: преобразование папки в зависимость. Возможно, вам следует придерживаться инструментов, которые поставляются с вашим языком программирования, чтобы решить эту проблему. Я добавил альтернативное решение с помощью npm, но я бы посоветовал посмотреть, как это можно сделать с помощью менеджера пакетов вашего языка. - person GabLeRoux; 22.10.2020
comment
Спасибо @GabLeRoux Я уже дважды использовал ваше описание для PHP-проекта, и он работал достаточно хорошо. Единственная проблема заключается в том, как настроить, чтобы подрепозиторий загружался затем в правильный подкаталог (надеюсь, с этими словами это понятнее, чем в моем вопросе). Я должен настроить один и создать другой файл composer.json, поэтому, вероятно, мне придется настроить его там. - person David; 22.10.2020
comment
Просто для git я подумал, что, возможно, есть способ «сказать» git, что подрепозиторий никогда не должен загружаться в корень родителя. Я знаю, что для cli существует возможность назначить каталог в качестве параметра, но это нужно вводить каждый раз и знать каждому пользователю. Возможно, есть возможность настроить его для постоянного использования git? - person David; 22.10.2020

Решение @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
person oodavid    schedule 22.06.2017
comment
он работает нормально, за исключением LFS (см. ответ от ls ниже), а также тегов: в моем случае он воссоздал весь родительский каталог в новом репозитории, поскольку теги были созданы для всего родительского каталога. мне это не нужно - person YaP; 12.10.2020

Решение 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'
person ls.    schedule 04.10.2017
comment
очень полезно, трудно найти! - person YaP; 12.10.2020