Отсоединить много подкаталогов в новый отдельный Git-репозиторий - PullRequest
114 голосов
/ 06 июня 2010

Этот вопрос основан на Отсоединение подкаталога в отдельный репозиторий Git

Вместо отсоединения одного подкаталога я хочу отсоединить пару. Например, мое текущее дерево каталогов выглядит так:

/apps
  /AAA
  /BBB
  /CCC
/libs
  /XXX
  /YYY
  /ZZZ

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

/apps
  /AAA
/libs
  /XXX

Аргумент --subdirectory-filter для git filter-branch не будет работать, потому что он избавляется от всего, кроме указанного каталога, при первом запуске. Я думал, что использование аргумента --index-filter для всех нежелательных файлов будет работать (хотя и утомительно), но если я попытаюсь запустить его несколько раз, я получу следующее сообщение:

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

Есть идеи? ТИА

Ответы [ 9 ]

127 голосов
/ 26 июля 2013

Вместо того, чтобы иметь дело с подоболочкой и использовать ext glob (как предложил kynan), попробуйте этот гораздо более простой подход:

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --prune-empty -- --all
33 голосов
/ 17 февраля 2017

Ручные шаги с простыми командами git

План состоит в том, чтобы разделить отдельные каталоги на свои собственные репозитории, а затем объединить их. В следующих шагах вручную не использовались скрипты, предназначенные для использования, но были понятные команды, и они могли помочь объединить дополнительные N подпапок в другой репозиторий.

Разделить

Предположим, ваш исходный репо: original_repo

1 - Сплит-приложения:

git clone original_repo apps-repo
cd apps-repo
git filter-branch --prune-empty --subdirectory-filter apps master

2 - Сплит libs

git clone original_repo libs-repo
cd libs-repo
git filter-branch --prune-empty --subdirectory-filter libs master

Продолжить, если у вас более 2 папок. Теперь у вас будет два новых и временных хранилища git.

Завоевать , объединяя приложения и библиотеки

3 - Подготовить новый репо:

mkdir my-desired-repo
cd my-desired-repo
git init

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

touch a_file_and_make_a_commit # see user's feedback
git add a_file_and_make_a_commit
git commit -am "at least one commit is needed for it to work"

При фиксированном временном файле команда merge в следующем разделе остановится, как и ожидалось.

Исходя из отзывов пользователей, вместо добавления случайного файла, такого как a_file_and_make_a_commit, вы можете добавить .gitignore или README.md и т. Д.

4 - сначала объединить репо приложений:

git remote add apps-repo ../apps-repo
git fetch apps-repo
git merge -s ours --no-commit apps-repo/master # see below note.
git read-tree --prefix=apps -u apps-repo/master
git commit -m "import apps"

Теперь вы должны увидеть каталог apps внутри вашего нового репозитория. git log должен показывать все соответствующие исторические коммиты.

Примечание: как отметил Крис ниже в комментариях, для более новой версии (> = 2.9) git вам нужно указать --allow-unrelated-histories с git merge

5 - Объединить libs repo следующим образом:

git remote add libs-repo ../libs-repo
git fetch libs-repo
git merge -s ours --no-commit libs-repo/master # see above note.
git read-tree --prefix=libs -u libs-repo/master
git commit -m "import libs"

Продолжайте, если у вас есть более 2 репо для слияния.

Ссылка: Объединить подкаталог другого хранилища с git

26 голосов
/ 21 октября 2011

Почему вы хотите запустить filter-branch более одного раза?Вы можете сделать все это за один раз, поэтому не нужно его принудительно (обратите внимание, что вам нужно включить extglob в вашей оболочке, чтобы это работало):

git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --prune-empty -- --all

Это должно избавить от всех измененийв нежелательных подкаталогах и сохраните все ваши ветви и коммиты (если только они не влияют только на файлы в сокращенных подкаталогах, в силу --prune-empty) - никаких проблем с дублирующимися коммитами и т. д.

После этой операции нежелательные каталогибыть перечисленным как неотслеживаемый git status.

$(ls ...) необходим, если extglob оценивается вашей оболочкой вместо фильтра индекса, который использует встроенную sh встроенную eval (где extglob недоступен).См. Как включить параметры оболочки в git? для получения дополнительной информации об этом.

19 голосов
/ 07 июня 2010

Отвечая на мой вопрос здесь ... после долгих проб и ошибок.

Мне удалось сделать это, используя комбинацию git subtree и git-stitch-repo. Эти инструкции основаны на:

Сначала я вытащил каталоги, которые хотел сохранить, в отдельный репозиторий:

cd origRepo
git subtree split -P apps/AAA -b aaa
git subtree split -P libs/XXX -b xxx

cd ..
mkdir aaaRepo
cd aaaRepo
git init
git fetch ../origRepo aaa
git checkout -b master FETCH_HEAD

cd ..
mkdir xxxRepo
cd xxxRepo
git init
git fetch ../origRepo xxx
git checkout -b master FETCH_HEAD

Затем я создал новый пустой репозиторий и импортировал / сшил в него последние два:

cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import

Это создает две ветви, master-A и master-B, каждая из которых содержит содержимое одного из сшитых репо. Объединить их и очистить:

git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A 
git branch -d master-B

Теперь я не совсем уверен, как / когда это происходит, но после первых checkout и pull код волшебным образом сливается с основной веткой (любое понимание того, что здесь происходит, приветствуется!)

Кажется, все работает как ожидалось, за исключением того, что, если я просматриваю историю фиксации newRepo, появляются дубликаты, когда набор изменений затрагивал как apps/AAA, так и libs/XXX. Если есть способ удалить дубликаты, то это было бы идеально.

7 голосов
/ 12 февраля 2015

Использовать git-расширение 'git split'

git splits - это bash-скрипт, который является оболочкой для git branch-filter, который я создал как расширение git на основе решения jkeating .

Это было сделано именно для этой ситуации. В случае вашей ошибки попробуйте использовать опцию git splits -f, чтобы принудительно удалить резервную копию. Поскольку git splits работает с новой веткой, она не будет перезаписывать вашу текущую ветку, поэтому резервное копирование является посторонним. Смотрите readme для более подробной информации и обязательно используйте его на копии / клоне вашего репо (на всякий случай!) .

  1. установка git splits.
  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 apps/AAA libs/ZZZ

  3. Создать где-нибудь пустой репо. Предположим, мы создали пустой репозиторий с именем xyz на GitHub, который имеет путь: 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

7 голосов
/ 18 февраля 2014

Я написал git-фильтр для решения именно этой проблемы. Он имеет фантастическое имя git_filter и находится на github здесь:

https://github.com/slobobaby/git_filter

Он основан на превосходном libgit2.

Мне нужно было разбить большой репозиторий с большим количеством коммитов (~ 100000), и решения, основанные на git filter-branch, заняли несколько дней. git_filter занимает минуту, чтобы сделать то же самое.

3 голосов
/ 06 июня 2010

Да. Принудительно перезаписать резервную копию, используя флаг -f при последующих вызовах к filter-branch, чтобы переопределить это предупреждение. :) В противном случае я думаю, что у вас есть решение (то есть, уничтожить нежелательный каталог за один раз с filter-branch).

1 голос
/ 07 августа 2018
git clone git@example.com:thing.git
cd thing
git fetch
for originBranch in `git branch -r | grep -v master`; do
    branch=${originBranch:7:${#originBranch}}
    git checkout $branch
done
git checkout master

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir1 dir2 .gitignore' --prune-empty -- --all

git remote set-url origin git@example.com:newthing.git
git push --all
0 голосов
/ 07 августа 2015

Удалите резервную копию из каталога .git в refs / original, как показано в сообщении. Каталог скрыт.

...