【问题标题】:git splitting repository by subfolder and retain all old branchesgit 按子文件夹拆分存储库并保留所有旧分支
【发布时间】:2014-01-12 12:02:53
【问题描述】:

我有一个包含 2 个目录和多个分支的 git repo,我想拆分它们并创建所有分支

`-- 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.

这需要更多的手动工作,我相信可能有一种快速的方法来实现这一点?

有什么想法吗???

【问题讨论】:

    标签: git git-submodules git-subtree git-filter-branch


    【解决方案1】:

    简答

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

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

    演练

    以下是一个以安全方式执行此操作的示例。您需要对将被隔离到其自己的 repo 中的每个子目录执行此操作,在本例中为 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-logs 访问。如果你想放弃这些阅读how to garbage collect commits

    一些额外的资源:

    【讨论】:

    • 感谢 LopSae.. 这对我帮助很大。这正是我正在寻找的。​​span>
    • 我试过你的方法,但标签没有被转移,我最终得到了一个包含标签的悬空分支。有没有办法将标签转移到过滤后的分支?
    • 使用-- --all 选项应该迁移分支和标签。如果您只想要标签,则存在 -- --tags 选项。请记住,如果您要标记未迁移的提交(例如,如果它从不触及子目录中的文件),则会打印警告并且标记将留在原处。
    • @LopSae,我尝试使用“git subtree split”,但我必须一次为每个分支做一次,这太耗时了.. 有什么快捷方式/技巧可以使用 subtree 命令所有分支/标签?
    • @Sridhar 我不知道。据我所知,仅在单个分支上进行拆分工作。
    【解决方案2】:

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

    #!/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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-15
      • 1970-01-01
      • 2017-11-19
      • 1970-01-01
      • 2013-03-03
      • 1970-01-01
      • 2011-01-05
      • 2011-02-17
      相关资源
      最近更新 更多