2009-05-28 2 views
105

Я начал проект несколько месяцев назад и сохранил все в основном каталоге. В моем основном каталоге «Проект» есть несколько подкаталогов, содержащих разные вещи: Проект/документ содержит документ, написанный в LaTeX Проект/исходный код/​​RailsApp содержит мое приложение rails.Как извлечь подкаталог git и сделать из него подмодуль?

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

Любые предложения?

+2

Также очень хороший ответ: http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository –

ответ

3

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

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

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

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

35

Оформить заказ git filter-branch.

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

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

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

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

+1

http://linux.die.net/man/1/git-filter -branch – schoetbi

+1

Это сработало для меня. Единственный недостаток, который я заметил, был результатом единственной ветви мастера со всеми коммитами. – aceofspades

+0

@aceofspades: почему это недостаток? – naught101

13

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

В основном, сделайте копию репозитория, затем используйте 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 файлов)

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

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

Самый большой очевидной проблемой является то, что оставляет все сообщения фиксации, даже если они не связаны с остальным файлом. Сценарий 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 силы аргумент запустить filter-branch снова что-нибудь в refs/original/ (который в основном резервное копирование)

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

Опять же, только запустите это на копии вашего хранилища! - но в целом, чтобы удалить все файлы, но «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

'git filter-branch' имеет (в настоящее время?) Встроенную опцию для удаления пустых коммитов, а именно' --prune-empty'. Лучшее руководство для 'git filter-branch' находится в ответах на этот вопрос: http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository – Blaisorblade

108

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

Установка
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.

+9

git subtree rocks! –

+3

Но разве не пункт git-поддерева, чтобы избежать использования подмодулей?Я имею в виду, что вы действительно являетесь автором git-поддерева (если нет столкновения с псевдонимами), но похоже, что git-поддерево изменено, хотя команда, которую вы показываете, по-прежнему действительна. Правильно ли я это понимаю? – Blaisorblade

+15

git-subtree теперь является частью git (если вы устанавливаете вкладчик) с 1.7.11 – Jeremy

Смежные вопросы