Отделить много подкаталогов в новом, отдельном репозитории Git

Этот вопрос основан на Отключить подкаталог в отдельный репозиторий 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

Любые идеи? ТИА

Ответ 1

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

Мне удалось сделать это, используя комбинацию 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. Если есть способ удалить дубликаты, то это будет идеально.

Ответ 2

Вместо того, чтобы иметь дело с подоболочкой и использовать 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

Как упомянуто void.pointer в его/ее комментарии, это удалит все, кроме apps/AAA и libs/XXX из текущего хранилища.

Ответ 3

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

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

Разделить

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

1 - Разделить приложения:

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

2 - Сплит-либы

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 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"

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

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

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

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

Ответ 4

Почему вы хотите запускать 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) - нет проблем с дублирующими коммитами и т.д.

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

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

Ответ 5

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

https://github.com/slobobaby/git_filter

Он основан на отличном libgit2.

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

Ответ 6

Использовать 'git splits' git extension

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

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

  • установите git splits.
  • Разделите каталоги на локальную ветвь #change into your repo directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ

  • Создайте пустой репо где-нибудь. Предположим, мы создали пустое репо под названием xyz на GitHub, у которого есть путь: [email protected]:simpliwp/xyz.git

  • Нажмите на новое репо. #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz [email protected]:simpliwp/xyz.git #push the branch to the empty repo master branch git push origin_xyz XYZ:master

  • Клонирование вновь созданного удаленного репо в новый локальный каталог
    #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 [email protected]:simpliwp/xyz.git

Ответ 7

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

Ответ 8

git clone [email protected]: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 [email protected]:newthing.git
git push --all

Ответ 9

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