git разделяет репозиторий на подпапки и сохраняет все старые ветки

У меня есть репозиторий git с двумя каталогами и несколькими ветками, я хочу разделить их и создать все ветки.

`-- Big-repo
    |-- dir1
    `-- dir2

Branches : branch1, branch2, branch3 ...

Что я хочу

Я хочу разделить dir1 и dir2 на два отдельных репозитория и сохранить ветки branch1, branch2... в обоих репозиториях.

dir1
Branches : branch1, branch2, branch3 ...

dir2
Branches : branch1, branch2, branch3 ...

Что я пробовал:

Я могу разделить их на 2 репозитория, используя

git subtree split -P dir1 -b dir1-only 
git subtree split -P dir2 -b dir2-only 

Но он не создает никаких ветвей после разделения.

Чтобы получить все ветки:

git checkout branch1 (in Big-repo)
git subtree split -p dir1 -b dir1-branch1

git checkout branch2 (in Big-repo)
git subtree split -p dir1 -b dir1-branch2

And push these branches to newly created repo.

Это требует больше ручных усилий, и я уверен, что может быть быстрый способ добиться этого?

Любые идеи???


person Sridhar    schedule 24.12.2013    source источник


Ответы (2)


Короткий ответ

git filter-branch предлагает именно те функции, которые вам нужны. С опцией --subdirectory-filter вы можете создать новый набор коммитов, где содержимое subDirectory находится в корне каталога.

git filter-branch --prune-empty --subdirectory-filter subDirectory -- --branches

Прохождение

Ниже приведен пример безопасного выполнения этого действия. Вам нужно выполнить это для каждого подкаталога, который будет изолирован в своем репозитории, в данном случае dir1.

Сначала клонируйте свой репозиторий, чтобы сохранить изменения изолированными:

git clone yourRemote dir1Clone
cd dir1Clone

Чтобы подготовить клонированный репозиторий, мы пересоздадим все удаленные ветки как локальные. Мы пропускаем ветку, начинающуюся с *, так как это текущая ветвь, которая в данном случае будет читаться как (no branch), так как мы находимся в безголовом состоянии:

# move to a headless state
# in order to delete all branches without issues
git checkout --detach

# delete all branches
git branch | grep --invert-match "*" | xargs git branch -D

Чтобы воссоздать все удаленные ветки локально, мы просматриваем результаты git branch --remotes. Мы пропускаем те, которые содержат ->, так как это не ветки:

# get all local branches for remote
git branch --remotes --no-color | grep --invert-match "\->" | while read remote; do
    git checkout --track "$remote"
done

# remove remote and remote branches
git remote remove origin

Наконец, запустите команду filter-branch. Это создаст новые коммиты со всеми коммитами, которые касаются подкаталога dir1. Все ветки, которые также касаются этого подкаталога, будут обновлены. В выводе будут перечислены все ссылки, которые не были обновлены, что имеет место для ветвей, которые вообще не касаются dir1.

# Isolate dir1 and recreate branches
# --prune-empty removes all commits that do not modify dir1
# -- --all updates all existing references, which is all existing branches
git filter-branch --prune-empty --subdirectory-filter dir1 -- --all

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

В качестве дополнительного последнего шага, если вам важен размер репозитория:

Даже если все ветки, в которых был обновлен ваш репозиторий, по-прежнему будут иметь все объекты исходного репозитория, но доступны только через ref-журналы. Если вы хотите удалить их, прочитайте как собирать коммиты

Некоторые дополнительные ресурсы:

person Maic López Sáenz    schedule 27.12.2013
comment
Спасибо LopSae .. Это мне очень помогло. Это именно то, что я ищу. - person Sridhar; 31.12.2013
comment
Я попробовал ваш метод, но теги не переносятся, и я получаю висячую ветку, содержащую теги. Есть ли способ перенести теги в отфильтрованную ветку? - person Uga Buga; 09.01.2014
comment
Использование опции -- --all должно мигрировать как ветки, так и теги. Если вам нужны только теги, существует опция -- --tags. Имейте в виду, что если вы помечаете коммит, который не был перенесен (например, если он никогда не касается файлов внутри подкаталога), будет напечатано предупреждение, а тег останется там, где он есть. - person Maic López Sáenz; 09.01.2014
comment
@LopSae, я пытался использовать разделение поддерева git, но мне нужно делать это для каждой ветки по одному разу, и это занимает слишком много времени ... есть ли какой-нибудь ярлык / трюк для использования команды поддерева для всех ветвей / тегов? - person Sridhar; 26.02.2014
comment
@ Шридхар Не то, чтобы я знал. Насколько я могу судить, сплит работает только на отдельных ветках. - person Maic López Sáenz; 27.02.2014
comment
Спасибо, что нашли время написать это пошаговое руководство. Очень полезно. - person skwidbreth; 29.11.2017

Этот скрипт делает работу за меня:

#!/bin/bash

set -e

if [ -z "$3" ]; then
        echo "usage: $0 /full/path/to/repository path/to/splitfolder/from/repository/root new_origin"
        exit
fi

repoDir=$1
folder=$2
newOrigin=$3

cd $repoDir

git checkout --detach
git branch | grep --invert-match "*" | xargs git branch -D

for remote in `git branch --remotes | grep --invert-match "\->"`
do
        git checkout --track $remote
        git add -vA *
        git commit -vam "Changes from $remote" || true
done

git remote remove origin
git filter-branch --prune-empty --subdirectory-filter $folder -- --all

#prune old objects
rm -rf .git/refs/original/*
git reflog expire --all --expire-unreachable=0
git repack -A -d
git prune

#upload to new remote
git remote add origin $newOrigin
git push origin master

for branch in `git branch | grep -v '\*'`
do
        git push origin $branch
done
person Arohi    schedule 25.09.2014