Git поддерево и несколько каталогов

У меня есть довольно большой репозиторий git, в котором есть каталог, в котором я поддерживаю библиотечный код. Каталог содержит несколько подкаталогов.

repo
+--- lib
|    +--- A
|    +--- B
...
|    +--- Z

Теперь предположим, что я хочу открыть исходные подкаталоги A,...,M и сохранить подкаталоги N,...,Z close sourced. Предположим также, что я хотел бы:

  • Храните A,...,M в одном хранилище с открытым исходным кодом. Причина этого в том, что каталоги A,...,M имеют взаимозависимости, и было бы путать их разделить на отдельные репозитории.
  • Сохранять структуру моего закрытого хранилища источника. Например, я мог бы создавать подкаталоги lib/pub и lib/pvt, но это имело бы каскадные эффекты, требующие изменения ссылок в другом месте или потребовало бы много символических ссылок (lib/A -> lib/pub/A).
  • У меня есть решение, похожее на git subtree, где я могу модифицировать код либо в моем закрытом исходном репозитории, либо в открытом исходном коде, и я могу легко синхронизировать изменения между двумя репозиториями.

Я искал решение как в stackoverflow, так и в google, но, похоже, не существует очевидного. Концептуально это то, что git subtree должно быть в состоянии сделать, но оно работает только с одним подкаталогом.

Я просмотрел git-subtree script с целью его изменения.

https://github.com/git/git/blob/master/contrib/subtree/git-subtree.sh

Мне кажется, что если я должен был изменить subtree_for_commit(), я должен был бы убедить git subtree split рассматривать больше, чем один каталог для разделения. Но моих знаний о git недостаточно, чтобы понять, что делает script, и изменять его, не нарушая ничего.

Если у вас есть решение для вышеупомянутой проблемы или любых других указателей при модификации git-subtree, пожалуйста, дайте мне знать.

Ответ 1

Разделение поддерева, смешанного с файлами из родительского проекта

Это, кажется, общий запрос, однако я не думаю, что существует простой ответ, когда папки смешаны вместе.

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

  • Создайте ветку с новым корнем для каталогов библиотек:

    git subtree split -P lib/ -b temp-br
    git checkout temp-br
    
  • Затем используйте что-то, чтобы перезаписать историю, чтобы удалить части, которые не являются частью библиотеки. Я не эксперт в этом, но я смог экспериментировать и нашел что-то вроде этого:

    git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch N O P Q R S T U V W X Y Z' HEAD
    

    Примечание. Возможно, вам придется удалить резервную копию, созданную ветвью фильтра, если вы выполните последовательные команды.

    git update-ref -d refs/original/refs/heads/temp-br
    
  • Наконец, просто создайте новое репо для библиотеки и потяните все, что осталось:

    cd <new-lib-repo>
    git init
    git pull <original-repo> temp-br
    

Ответ 2

Вот сценарий оболочки, основанный на git subtree, он намного быстрее, чем решения, основанные на git filter-branch --tree-filter; его побочный эффект - это несколько дополнительных коммитов git mv и git merge, которые будут добавлены к финальному HEAD. Если вы согласны с этими дополнительными пустыми коммитами, попробуйте:

ids=0
lists=(\
    "a/b" \
    "c/d/e" \
)
# subtree each path
for dir in ${lists[@]}
do
    echo git subtree split -P $dir -b split_dir_$ids
    git subtree split -P $dir -b split_dir_$ids
    ((ids++))
done

# restore folder structure
for (( idx=0; idx < ${#lists[@]}; idx++ ))
do
    git checkout split_dir_$idx
    dir=${lists[$idx]}
    mkdir -p $dir
    dirPrefix=${$dir%%/*}
    find . -maxdepth 1 ! -name $dirPrefix -and ! -name '\.*' \
        -exec git mv {} $dir \;
done

# merge
git checkout split_dir_0
for (( idx=1; idx < ${#lists[@]}; idx++ ))
do
    git merge -q split_dir_$idx
done

git push -u 'target remote' 'target branch'

Ответ 3

Используйте git subtree add

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

cd /repos/big-repo

# split out A..M branches
for N in {A..M}; do
  git subtree split --prefix=lib/$N --branch=split-$N
done

# create new repo
mkdir /repos/am-repo
cd /repos/am-repo
git init

# commit something or git-subtree add will complain and fail
touch .gitignore; git add .; git commit -m "begin history revision"

# split-in A..M branches
for N in {A..M}; do
  git subtree add --prefix=lib/$N ../big-repo split-$N
done

Ответ 4

Когда у вас есть как подкаталоги, так и файлы в каталоге src, вы хотите разбить на отдельный репозиторий, который позже станет подмодулем, ответов было мало. Предположим, вы хотите, чтобы dir2 и file2 переместились в новую репо srcpublic, а затем в исходное репо,

git mv src/file2 src/dir2; git разделение поддерева -P dir2 -b branch_dir2

В новом репо,   поддерево pull/dir2 branch_dir2;    git mv dir2/file2../

Новое репо: srcpublic - file2, dir2

Оригинальное репо: src - file1, file2, dir1, dir2

Когда есть десятки папок и файлов, это помогает помещать команды в script.