Як витягнути підкаталог git і зробити з нього підмодуль?


120

Я розпочав проект кілька місяців тому і зберігав усе в основному каталозі. У моєму головному каталозі "Проект" є кілька підкаталогів, що містять різні речі: Проект / папір містить документ, написаний у LaTeX Project / sourcecode / RailsApp, містить мій додаток рейлів.

"Project" GITified, і в каталозі "paper" та "RailsApp" було багато комісій. Тепер, як я хотів би використовувати cruisecontrol.rb для свого "RailsApp", мені цікаво, чи є спосіб зробити підмодуль з "RailsApp", не втрачаючи історії.


2
Також дуже хороший відповідь: stackoverflow.com/questions/359424 / ...
Rehno Lindeque

Відповіді:


123

Сьогодні існує набагато простіший спосіб зробити це, ніж вручну за допомогою 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 git@github.com: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 git@github.com:my-user/new-project.git foo

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


10
git subtree скелі!
Саймон Вудсайд

3
Але хіба не сенс git-subtree уникати використання підмодулів? Я маю на увазі, ти справді автор git-subtree (якщо не трапляється зіткнення прізвиська), але це схоже на те, що git-subtree змінено, хоча команда, яку ви показуєте, здається, все ще дійсна. Чи правильно я це розумію?
Blaisorblade

18
git-subtree тепер є частиною git (якщо встановити contrib) станом на 1.7.11
Джеремі

8
Добре git rm -rf ./fooвидаляє fooз , HEADале не фільтрує my-project«s повна історія. Потім, git submodule add git@github.com:my-user/new-project.git fooлише робить fooпідмодуль, починаючи з HEAD. У цьому відношенні сценарій filter-branchє вищим, оскільки він дозволяє досягти "робити так, ніби субдір був підмодулем з самого початку"
Григорій Пакош

THX для цього - git subtree docs трохи збиває з пантелику, і це (для мене) найбільш очевидно корисне, що я хотів зробити з цим ...
hwjp

38

Оформити замовлення git filter-відділення .

У Examplesрозділі довідкової сторінки показано, як витягнути підкаталог у власний проект, зберігаючи всю його історію та видаляючи історію інших файлів / каталогів (саме те, що ви шукаєте).

Щоб переписати сховище таким чином, щоб він виглядав як foodir/корінь проекту, і видалити всю іншу історію:

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

Таким чином, ви можете, наприклад, перетворити підкаталог бібліотеки у власне сховище.
Зверніть увагу на --те, що розділяє filter-branchпараметри від параметрів редагування та --allпереписати всі гілки та теги.


1
Це добре спрацювало для мене. Єдиним недоліком, який я помітив, був результат єдиної ведучої гілки з усіма зобов'язаннями.
ацеофспади

@aceofspades: чому це мінус?
naught101

2
Для мене вся суть вилучення комітетів з git repo полягає в тому, що я хочу зберегти історію.
ацеофспад

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-здійснює , виправляє це ..

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; 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 "$@"; else git commit-tree "$@"; fi'

4
git filter-branchє (в наш час?) вбудований варіант для видалення порожніх комітетів, а саме --prune-empty. Кращий гід по git filter-branchу відповідях на це питання: stackoverflow.com/questions/359424 / ...
Blaisorblade

4

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

Спочатку перейдіть до Git Bash до кореня git repo, який потрібно розділити. У моєму прикладі ось що~/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 git@github.com:myUsername/newRepo.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add git@github.com: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

3

Якщо ви хочете перенести деяку підмножину файлів у нове сховище, але зберегти історію, ви в основному закінчите зовсім нову історію. Це, як це було б, полягає в наступному:

  1. Створіть новий сховище.
  2. Для кожної редакції вашого старого сховища об'єднайте зміни свого модуля в нове сховище. Це створить "копію" вашої існуючої історії проекту.

Автоматизувати це слід дещо просто, якщо ви не заперечуєте над написанням невеликого, але волохатого сценарію. Відверто, так, але також боляче. Люди раніше переписували історію в Git, ви можете зробити пошук.

Як варіант: клонуйте сховище та видаліть папір у клоні, видаліть програму в оригіналі. Це займе одну хвилину, це гарантовано спрацює, і ви можете повернутися до більш важливих речей, ніж намагатися очистити свою історію git. І не хвилюйтеся з приводу місця на жорсткому диску, зайнятого зайвими копіями історії.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.