git按子文件夹拆分存储库并保留所有旧分支

Sri*_*har 26 git git-submodules git-filter-branch git-subtree

我有一个带有2个目录和多个分支的git仓库,我想拆分它们并创建所有分支

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

Branches : branch1, branch2, branch3 ...
Run Code Online (Sandbox Code Playgroud)

我想要的是

我想将dir1和dir2拆分为两个独立的repos并在两个存储库中保留branch branch1,branch2 ....

dir1
Branches : branch1, branch2, branch3 ...

dir2
Branches : branch1, branch2, branch3 ...
Run Code Online (Sandbox Code Playgroud)

我尝试了什么:

我能够将它们分成2个回购

git subtree split -P dir1 -b dir1-only 
git subtree split -P dir2 -b dir2-only 
Run Code Online (Sandbox Code Playgroud)

但是,分离后不会创建任何分支.

要获得所有分支:

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.
Run Code Online (Sandbox Code Playgroud)

这涉及更多的手动工作,我相信可能有一个快速的方法来实现这一目标?

有任何想法吗???

Lop*_*Sae 44

简短的回答

git filter-branch提供您想要的功能.使用该--subdirectory-filter选项,您可以创建一组新的提交,其中的内容subDirectory位于目录的根目录中.

git filter-branch --prune-empty --subdirectory-filter subDirectory -- --branches
Run Code Online (Sandbox Code Playgroud)

演练

以下是以安全的方式执行此操作的示例.在这种情况下,您需要为每个子目录执行此操作,这些子目录将被隔离到其自己的存储库中dir1.

首先克隆您的存储库以保持隔离的更改:

git clone yourRemote dir1Clone
cd dir1Clone
Run Code Online (Sandbox Code Playgroud)

要准备克隆的存储库,我们将重新创建所有远程分支作为本地分支.我们跳过*从那开始的那个,因为那是当前的分支,在这种情况下,(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
Run Code Online (Sandbox Code Playgroud)

要在本地重新创建所有远程分支,我们将查看结果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
Run Code Online (Sandbox Code Playgroud)

最后运行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
Run Code Online (Sandbox Code Playgroud)

在此之后,您将拥有一组新的提交,这些提交dir1位于存储库的根目录下.只需添加您的遥控器来推送新提交,或者将它们全部用作新的存储库.

如果您关心存储库大小,则作为额外的最后一步:

即使更新了存储库的所有分支仍然具有原始存储库的所有对象,只能通过ref-logs访问.如果你想删除这些读取如何垃圾收集提交

一些额外的资源:


小智 6

这个脚本为我完成了这项工作:

#!/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
Run Code Online (Sandbox Code Playgroud)