Отсоединить (переместить) подкаталог в отдельный Git-репозиторий - PullRequest
1673 голосов
/ 11 декабря 2008

У меня есть Git репозиторий, который содержит несколько подкаталогов. Теперь я обнаружил, что одно из подкаталогов не связано с другим и должно быть отсоединено от отдельного хранилища.

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

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

Просто чтобы прояснить, у меня есть следующая структура:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

Но я бы хотел вместо этого:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

Ответы [ 24 ]

6 голосов
/ 12 ноября 2014

Правильный путь теперь следующий:

git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

GitHub теперь даже имеет небольшую статью о таких случаях.

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

Итак, ваш алгоритм должен быть:

  1. клонировать ваше удаленное хранилище в другой каталог
  2. с использованием git filter-branch оставить только файлы в некотором подкаталоге, нажать на новый пульт
  3. создать коммит для удаления этого подкаталога из вашего исходного удаленного репо
5 голосов
/ 19 сентября 2016

Похоже, что большинство (все?) Ответов здесь полагаются на какую-то форму git filter-branch --subdirectory-filter и тому подобное. Однако в большинстве случаев это может работать «чаще всего», например, когда вы переименовали папку, например:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

Если вы используете обычный стиль фильтра git для извлечения «move_me_renamed», вы потеряете историю изменений файлов, которая произошла с того времени, когда она изначально была move_this_dir ( ref ).

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

  1. Локальное клонирование многомодульного проекта
  2. Ветки - проверьте что там: git branch -a
  3. Выполните проверку для каждой ветви, которая будет включена в разделение, чтобы получить локальную копию на вашей рабочей станции: git checkout --track origin/branchABC
  4. Сделайте копию в новом каталоге: cp -r oldmultimod simple
  5. Перейти в новую копию проекта: cd simple
  6. Избавьтесь от других модулей, которые не нужны в этом проекте:
  7. git rm otherModule1 other2 other3
  8. Теперь остается только подкаталог целевого модуля
  9. Избавьтесь от поддиректории модуля, чтобы корень модуля стал новым корнем проекта
  10. git mv moduleSubdir1/* .
  11. Удалить подпапку реликвии: rmdir moduleSubdir1
  12. Проверить изменения в любой точке: git status
  13. Создайте новый репозиторий git и скопируйте его URL, чтобы указать на него этот проект:
  14. git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
  15. Убедитесь, что это хорошо: git remote -v
  16. Нажмите изменения до удаленного репо: git push
  17. Зайдите в удаленный репозиторий и проверьте, все ли там
  18. Повторите это для любой другой необходимой ветки: git checkout branch2

Это следует за документом github "Разделение подпапки в новом хранилище" шаги 6-11, чтобы подтолкнуть модуль к новому репо.

Это не сэкономит вам места в папке .git, но сохранит всю историю изменений этих файлов даже при переименованиях. И это может не стоить того, если не будет потеряно «много» истории и т. Д. Но по крайней мере вы гарантированно не потеряете старые коммиты!

4 голосов
/ 07 февраля 2012

Для чего стоит, вот как использовать GitHub на компьютере с Windows. Допустим, у вас есть клонированный репозиторий в C:\dir1. Структура каталогов выглядит следующим образом: C:\dir1\dir2\dir3. Каталог dir3 - это каталог, в котором я хочу стать новым отдельным репо.

Github:

  1. Создайте свой новый репозиторий: MyTeam/mynewrepo

Bash Prompt:

  1. $ cd c:/Dir1
  2. $ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
    Возвращено: Ref 'refs/heads/master' was rewritten (для справки: dir2 / dir3 чувствителен к регистру.)

  3. $ git remote add some_name git@github.com:MyTeam/mynewrepo.git
    git remote add origin etc. не сработало, вернул "remote origin already exists"

  4. $ git push --progress some_name master

4 голосов
/ 10 марта 2014

У меня была именно эта проблема, но все стандартные решения, основанные на git filter-branch, были чрезвычайно медленными. Если у вас небольшой репозиторий, то это может не быть проблемой, это было для меня. Я написал другую программу фильтрации git, основанную на libgit2, которая в качестве первого шага создает ветки для каждой фильтрации основного хранилища, а затем отправляет их для очистки хранилищ в качестве следующего шага. В моем репозитории (500Mb 100000 коммитов) стандартные методы git filter-branch заняли несколько дней. Моя программа занимает минуты, чтобы выполнить ту же фильтрацию.

У него невероятное имя git_filter и он живет здесь:

https://github.com/slobobaby/git_filter

на GitHub.

Надеюсь, это кому-нибудь пригодится.

3 голосов
/ 11 февраля 2015

Легкий путь

  1. установка git splits. Я создал его как расширение git, основанное на решении jkeating .
  2. Разделить каталоги на локальную ветвь #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ<br> #split multiple directories into new branch XYZ git splits -b XYZ XY1 XY2

  3. Создать где-нибудь пустой репо. Предположим, мы создали на GitHub пустой репозиторий с именем xyz, путь которого: git@github.com:simpliwp/xyz.git

  4. Нажмите на новый репо. #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. Клонировать вновь созданное удаленное хранилище в новый локальный каталог
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone git@github.com:simpliwp/xyz.git

3 голосов
/ 31 мая 2013

Как я упомянул выше , мне пришлось использовать обратное решение (удаление всех коммитов, не касаясь моего dir/subdir/targetdir), которое, казалось, работало довольно хорошо, удаляя около 95% коммитов (по желанию). Однако остаются две небольшие проблемы.

FIRST , filter-branch проделали огромную работу по удалению коммитов, которые вводят или модифицируют код, но, очевидно, коммитов слияния находятся ниже его места в Gitiverse.

Это косметическая проблема, с которой я, вероятно, могу жить (он говорит ... медленно отступая с отведенными глазами) .

ВТОРОЕ несколько оставшихся коммитов в значительной степени ALL продублированы! Кажется, я приобрел второй, избыточный график, который охватывает почти всю историю проекта. Интересная вещь (которую вы можете увидеть из рисунка ниже) состоит в том, что мои три локальных ветви не все находятся на одной временной шкале (что, разумеется, почему оно существует, а не просто сбор мусора).

Единственное, что я могу себе представить, это то, что одним из удаленных коммитов был, возможно, единственный коммит слияния, который filter-branch фактически удалил , и который создавал параллельную временную шкалу как каждую теперь не слитую цепь взял свою собственную копию коммитов. ( пожимает плечами Где мои ТАРДИ?) Я почти уверен, что смогу решить эту проблему, хотя я бы действительно с удовольствием понял, как это произошло.

В случае сумасшедшего mergefest-O-RAMA я, скорее всего, оставлю его в покое, так как он так прочно укоренился в моей истории коммитов - угрожая мне, когда я подхожу, - кажется, это не так. на самом деле вызывает какие-то не косметические проблемы и потому что это довольно симпатично в Tower.app.

3 голосов
/ 28 октября 2010

Используйте эту команду фильтра для удаления подкаталога, сохраняя при этом ваши теги и ветви:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all
3 голосов
/ 31 августа 2017

Я рекомендую Руководство GitHub по разбиению подпапок в новый репозиторий . Шаги похожи на ответ Павла , но я обнаружил, что их инструкции легче понять.

Я изменил инструкции, чтобы они применялись для локального репозитория, а не для размещения на GitHub.


Разделение подпапки в новый репозиторий

  1. Open Git Bash.

  2. Измените текущий рабочий каталог на каталог, в котором вы хотите создать свой новый репозиторий.

  3. Клонировать репозиторий, содержащий подпапку.

git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
  1. Измените текущий рабочий каталог на ваш клонированный репозиторий.

cd REPOSITORY-NAME
  1. Чтобы отфильтровать подпапку от остальных файлов в хранилище, запустите git filter-branch, предоставив эту информацию:
    • FOLDER-NAME: Папка в вашем проекте, из которой вы хотите создать отдельный репозиторий.
      • Совет. Пользователям Windows следует использовать / для разделения папок.
    • BRANCH-NAME: ветвь по умолчанию для вашего текущего проекта, например, master или gh-pages.

git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME 
# Filter the specified branch in your directory and remove empty commits
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
Ref 'refs/heads/BRANCH-NAME' was rewritten
2 голосов
/ 06 января 2016

Проверьте проект git_split на https://github.com/vangorra/git_split

Превратите каталоги git в свои собственные репозитории на своем месте. Не поддельное смешное дело. Этот скрипт возьмет существующий каталог в вашем git-репозитории и превратит этот каталог в независимый собственный репозиторий. Попутно он скопирует всю историю изменений для предоставленного вами каталога.

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.
2 голосов
/ 12 июня 2009

Вам может понадобиться что-то вроде "git reflog expire --expire = now --all" перед сборкой мусора, чтобы фактически очистить файлы. git filter-branch просто удаляет ссылки в истории, но не удаляет записи reflog, которые содержат данные. Конечно, сначала проверьте это.

Мое использование диска резко упало при этом, хотя мои начальные условия были несколько иными. Возможно --subdirectory-filter отрицает эту необходимость, но я сомневаюсь в этом.

...