Как извлечь подкаталог git и сделать из него подмодуль?

Я начал проект несколько месяцев назад и сохранил все в основной директории. В моем основном каталоге "Проект" есть несколько подкаталогов, содержащих разные вещи: Project/paper содержит документ, написанный в LaTeX Project/sourcecode/RailsApp, содержащий мое приложение rails.

"Проект" является GITified, и в обеих папках "бумага" и "RailsApp" было много компромиссов. Теперь, поскольку я хотел бы использовать cruisecontrol.rb для моего "RailsApp", мне интересно, есть ли способ сделать субмодуль из "RailsApp" без потери истории.

Ответ 1

В настоящее время существует гораздо более простой способ сделать это, чем вручную, используя git filter-branch: git subtree

Монтаж

ПРИМЕЧАНИЕ git-subtree теперь является частью git (если вы устанавливаете contrib) начиная с 1.7.11, так что вы, возможно, уже установили его. Вы можете проверить с помощью git subtree.


Чтобы установить git-subtree из исходного кода (для более старых версий git):

git clone https://github.com/apenwarr/git-subtree.git

cd git-subtree
sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree

Или, если вы хотите, чтобы страницы руководства и все

make doc
make install

использование

Разделите большее на более мелкие куски:

# Go into the project root
cd ~/my-project

# Create a branch which only contains commits for the children of 'foo'
git subtree split --prefix=foo --branch=foo-only

# Remove 'foo' from the project
git rm -rf ./foo

# Create a git repo for 'foo' (assuming we already created it on github)
mkdir foo
pushd foo
git init
git remote add origin [email protected]:my-user/new-project.git
git pull ../ foo-only
git push origin -u master
popd

# Add 'foo' as a git submodule to 'my-project'
git submodule add [email protected]:my-user/new-project.git foo

Для получения подробной документации (git-subtree.txt страница), пожалуйста, прочитайте git-subtree.txt.

Ответ 2

Оформить заказ git ветвь фильтра.

Раздел Examples на странице руководства показывает, как извлечь подкаталог в собственный проект, сохраняя при этом всю историю и отбрасывая историю других файлов/каталогов (именно то, что вы ищете).

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

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

Таким образом, вы можете, например, превратить подкаталог библиотеки в собственный репозиторий.
Обратите внимание на --, который отделяет опции filter-branch от параметров ревизии и --all, чтобы переписать все ветки и теги.

Ответ 3

Один из способов сделать это - инвертировать - удалить все, кроме файла, который вы хотите сохранить.

В основном, сделать копию репозитория, а затем git filter-branch удалить все, кроме файла/папки, которые вы хотите сохранить.

Например, у меня есть проект, из которого я хочу извлечь файл tvnamer.py в новый репозиторий:

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD

Это использует git filter-branch --tree-filter для прохождения каждой фиксации, запускает команду и возобновляет содержимое результирующих каталогов. Это крайне разрушительно (так что вы должны делать это только на копии своего репозитория!), И может занять некоторое время (около 1 минуты в репозитории с 300 коммитов и около 20 файлов)

Вышеприведенная команда запускает только следующую оболочку script для каждой ревизии, которую вам нужно будет, конечно, изменить (чтобы исключить ваш подкаталог вместо tvnamer.py):

for f in *; do
    if [ $f != "tvnamer.py" ]; then
        rm -rf $f;
    fi;
done

Самая большая очевидная проблема заключается в том, что она оставляет все сообщения фиксации, даже если они не связаны с остальным файлом. script git-remove-empty-commits, исправляет это.

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "[email protected]"; else git commit-tree "[email protected]"; fi'

Вам нужно снова использовать параметр -f force filter-branch с чем-либо в refs/original/ (который в основном представляет собой резервную копию)

Конечно, это никогда не будет идеальным, например, если ваши сообщения о фиксации упоминают другие файлы, но он близок к тому, что ток git позволяет (насколько мне известно).

Опять же, только запустите это на копии вашего репозитория! - но в итоге, чтобы удалить все файлы, но "thisismyfilename.txt":

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "[email protected]"; else git commit-tree "[email protected]"; fi'

Ответ 4

Ответы CoolAJ86 и apenwarr очень похожи. Я ходил туда-сюда между двумя, пытаясь понять, чего не хватает ни в одном из них. Ниже приведена их комбинация.

Сначала перейдите в Git Bash к корню git-репо, который нужно разделить. В моем примере это ~/Documents/OriginalRepo (master)

# move the folder at prefix to a new branch
git subtree split --prefix=SubFolderName/FolderToBeNewRepo --branch=to-be-new-repo

# create a new repository out of the newly made branch
mkdir ~/Documents/NewRepo
pushd ~/Documents/NewRepo
git init
git pull ~/Documents/OriginalRepo to-be-new-repo

# upload the new repository to a place that should be referenced for submodules
git remote add origin [email protected]:myUsername/newRepo.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add [email protected]:myUsername/newRepo.git SubFolderName/FolderToBeNewRepo
git branch --delete --force to-be-new-repo

Ниже приведена копия выше с заменой настраиваемых имен и использованием https. Корневая папка теперь ~/Documents/_Shawn/UnityProjects/SoProject (master)

# move the folder at prefix to a new branch
git subtree split --prefix=Assets/SoArchitecture --branch=so-package

# create a new repository out of the newly made branch
mkdir ~/Documents/_Shawn/UnityProjects/SoArchitecture
pushd ~/Documents/_Shawn/UnityProjects/SoArchitecture
git init
git pull ~/Documents/_Shawn/UnityProjects/SoProject so-package

# upload the new repository to a place that should be referenced for submodules
git remote add origin https://github.com/Feddas/SoArchitecture.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./Assets/SoArchitecture
git submodule add https://github.com/Feddas/SoArchitecture.git
git branch --delete --force so-package

Ответ 5

Если вы хотите перенести некоторое подмножество файлов в новый репозиторий, но сохраните историю, вы, в основном, получите совершенно новую историю. То, как это работает, в основном выглядит следующим образом:

  • Создать новый репозиторий.
  • Для каждой ревизии старого хранилища объедините изменения в свой модуль в новый репозиторий. Это создаст "копию" вашей существующей истории проекта.

Это должно быть несколько просто, чтобы автоматизировать это, если вы не возражаете писать небольшой, но волосатый script. Прямо, да, но и больно. В прошлом люди переписывали историю в Git, вы можете выполнить поиск.

Альтернативно: клонировать репозиторий и удалять бумагу в клоне, удалите приложение в оригинале. Это займет одну минуту, это гарантирует работу, и вы можете вернуться к более важным вещам, чем пытаться очистить историю Git. И не беспокойтесь о пространстве на жестком диске, занятом избыточными копиями истории.