Отделить много подкаталогов в новом, отдельном репозитории 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, как это предлагает сообщение. Каталог скрыто.