Как извлечь подкаталог 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. И не беспокойтесь о пространстве на жестком диске, занятом избыточными копиями истории.