关于Git子树:分离(移动)子目录到单独的Git存储库

Detach (move) subdirectory into separate Git repository

我有一个Git存储库,其中包含许多子目录。现在我发现其中一个子目录与另一个子目录无关,应该分离到单独的存储库中。

如何在子目录中保存文件的历史记录的同时做到这一点?

我想我可以制作一个克隆并删除每个克隆不需要的部分,但我想这会在签出旧版本等时提供完整的树。这可能是可以接受的,但我更愿意假装两个存储库没有共享的历史。

为了清楚起见,我有以下结构:

1
2
3
4
5
XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想改为:

1
2
3
4
5
6
7
XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/


简单的方式和贸易;

事实证明,这是一个非常常见和有用的实践,Git的霸主让它变得非常容易,但你必须有一个新版本的Git(>1.7.11 2012年5月)。有关如何安装最新Git的信息,请参阅附录。此外,下面的演练中还有一个真实的示例。好的。

  • 准备旧回购好的。

    1
    2
    3
    pushd <big-repo>
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd

    注意:不能包含前导或尾随字符。例如,名为subproject的文件夹必须作为subproject而不是./subproject/传递。好的。

    Windows用户注意:当文件夹深度大于1时,必须有*nix样式的文件夹分隔符(/)。例如,名为path1\path2\subproject的文件夹必须作为path1/path2/subproject传递。好的。

  • 创建新回购好的。

    1
    2
    3
    4
    5
    mkdir <new-repo>
    pushd <new-repo>

    git init
    git pull </path/to/big-repo> <name-of-new-branch>
  • 将新回购协议链接到Github或任何地方好的。

    1
    2
    git remote add origin <[email protected]:my-user/new-repo.git>
    git push origin -u master
  • 清除(如果需要)好的。

    1
    2
    3
    4
    popd # get out of <new-repo>
    pushd <big-repo>

    git rm -rf <name-of-folder>

    注意:这会将所有历史引用保留在存储库中。如果您确实担心提交了密码,或者需要减小.git文件夹的文件大小,请参阅下面的附录。好的。

  • …好的。演练

    这些步骤与上面的步骤相同,但是遵循我的存储库的确切步骤,而不是使用。好的。

    下面是我在节点中实现JavaScript浏览器模块的一个项目:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    tree ~/Code/node-browser-compat

    node-browser-compat
    ├── ArrayBuffer
    ├── Audio
    ├── Blob
    ├── FormData
    ├── atob
    ├── btoa
    ├── location
    └── navigator

    我想把一个文件夹btoa分割成一个单独的git存储库好的。

    1
    2
    3
    pushd ~/Code/node-browser-compat/
    git subtree split -P btoa -b btoa-only
    popd

    我现在有了一个新的分支,btoa-only,它只对btoa进行了承诺,我想创建一个新的存储库。好的。

    1
    2
    3
    4
    mkdir ~/Code/btoa/
    pushd ~/Code/btoa/
    git init
    git pull ~/Code/node-browser-compat btoa-only

    接下来,我在GitHub或BitBucket上创建一个新的repo,或者添加它是origin(btw,"origin"只是一个约定,而不是命令的一部分—您可以将其称为"远程服务器"或者您喜欢的任何东西)。好的。

    1
    2
    git remote add origin [email protected]:node-browser-compat/btoa.git
    git push origin -u master

    快乐的一天!好的。

    注:如果您使用README.md.gitignoreLICENSE创建了回购,则需要先拉:好的。

    1
    2
    git pull origin -u master
    git push origin -u master

    最后,我要从更大的报告中删除文件夹好的。

    1
    git rm -rf btoa

    …好的。附录OS X上的最新Git

    要获取最新版本的Git:好的。

    1
    brew install git

    要获取BREW for OS X:好的。

    HTTP//BURW.SH好的。Ubuntu上的最新Git

    1
    2
    3
    sudo apt-get update
    sudo apt-get install git
    git --version

    如果这不起作用(你有一个非常老的Ubuntu版本),试试看。好的。

    1
    2
    3
    sudo add-apt-repository ppa:git-core/ppa
    sudo apt-get update
    sudo apt-get install git

    如果还是不行,试试看好的。

    1
    2
    3
    4
    sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
    sudo ln -s \
    /usr/share/doc/git/contrib/subtree/git-subtree.sh \
    /usr/lib/git-core/git-subtree

    感谢Rui.Araujo的评论。好的。清除您的历史记录

    默认情况下,从Git中删除文件实际上并没有从Git中删除它们,只是承诺不再存在。如果要实际删除历史引用(即您已提交密码),则需要执行以下操作:好的。

    1
    git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD

    之后,您可以检查您的文件或文件夹是否不再显示在Git历史记录中。好的。

    1
    git log -- <name-of-folder> # should show nothing

    但是,您不能将删除"推送"到GitHub等。如果你尝试的话,你会得到一个错误,你必须在你能做到之前,先到江户×1〔16〕,然后你就可以恢复你历史上的一切。好的。

    因此,如果您想从"原始"中删除历史记录(即从GitHub、BitBucket等中删除历史记录),则需要删除repo并重新推送修剪过的repo副本。但是等等-还有更多!-如果你真的很担心删除密码或类似的东西,你需要修剪备份(见下文)。好的。使.git更小

    前面提到的删除历史记录命令仍然会留下一堆备份文件——因为Git太仁慈了,无法帮助您意外破坏您的repo。它最终会在数天和数月内删除孤立的文件,但会将它们保留一段时间,以防您意识到您意外删除了不想删除的内容。好的。

    因此,如果您真的想清空垃圾箱以立即减少回购的克隆大小,那么您必须执行所有这些非常奇怪的操作:好的。

    1
    2
    3
    4
    5
    6
    7
    rm -rf .git/refs/original/ && \
    git reflog expire --all && \
    git gc --aggressive --prune=now

    git reflog expire --all --expire-unreachable=0
    git repack -A -d
    git prune

    也就是说,我建议不要执行这些步骤,除非你知道你需要-以防万一你删掉了错误的子目录,你知道吗?当您按下repo时,备份文件不应该被克隆,它们只会出现在您的本地副本中。好的。信用卡

    • http://psionides.eu/2010/02/04/sharing-code-between-projects-with-git-subtree/
    • 从git中永久删除目录
    • http://blogs.atlassian.com/2013/05/alternations-to-git-submodule-git-subtree/
    • 如何从我的git repo中删除未引用的blob

    好啊。


    更新:这个过程很常见,Git团队使用一个新工具git subtree简化了它。请参见此处:将子目录分离(移动)到单独的Git存储库中

    您想克隆您的存储库,然后使用git filter-branch标记除要在新repo中进行垃圾收集的子目录之外的所有内容。

  • 要克隆本地存储库,请执行以下操作:

    1
    git clone /XYZ /ABC

    (注意:存储库将使用硬链接进行克隆,但这不是问题,因为硬链接文件本身不会被修改-将创建新的文件。)

  • 现在,让我们保留要重写的有趣分支,然后删除源代码以避免将其推到那里,并确保源代码不会引用旧的提交:

    1
    2
    3
    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin

    或者对于所有远程分支:

    1
    2
    3
    cd /ABC
    for i in $(git branch -r | sed"s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
  • 现在,您可能还需要删除与子项目无关的标记;您也可以稍后执行,但可能需要再次修剪您的repo。我没有这样做,所有标签都得到了一个WARNING: Ref 'refs/tags/v0.1' is unchanged(因为它们都与子项目无关);此外,删除这些标签后,更多的空间将被回收。显然,EDOCX1[1]应该能够重写其他标签,但我无法验证这一点。如果要删除所有标签,请使用git tag -l | xargs git tag -d

  • 然后使用filter branch和reset排除其他文件,以便修剪它们。我们还要添加--tag-name-filter cat --prune-empty来删除空提交并重写标记(注意,这将需要去掉它们的签名):

    1
    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all

    或者,只重写头分支并忽略标记和其他分支:

    1
    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
  • 然后删除备份刷新,以便真正回收空间(尽管现在操作具有破坏性)

    1
    2
    3
    4
    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now

    现在您有了ABC子目录的本地Git存储库,它的所有历史都保存了下来。

  • 注:在大多数情况下,git filter-branch实际上应该增加参数-- --all。是的,那确实是 space all。这需要是命令的最后一个参数。正如Matli发现的那样,这将保留新回购中包含的项目分支和标记。

    编辑:下面评论中的各种建议都被合并进来,以确保,例如,存储库实际上被缩小了(以前并不总是这样)。


    Paul的答案创建了一个包含/abc的新存储库,但没有从/xyz中删除/abc。以下命令将从/xyz中删除/abc:

    1
    git filter-branch --tree-filter"rm -rf ABC" --prune-empty HEAD

    当然,首先在"clone——no hardlinks"存储库中测试它,然后按照paul列出的reset、gc和prune命令进行测试。


    我发现,为了正确地从新存储库中删除旧的历史记录,您必须在执行filter-branch步骤之后再做一些工作。

  • 执行克隆和筛选:

    1
    2
    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
  • 删除对旧历史的所有引用。"origin"跟踪您的克隆,"original"是筛选分支保存旧内容的位置:

    1
    2
    3
    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
  • 即使现在,您的历史可能会被卡在一个文件包文件,而fsck不会接触。将其撕成碎片,创建新的packfile并删除未使用的对象:

    1
    git repack -ad
  • 在过滤器分支手册中对此有一个解释。


    编辑:添加了bash脚本。

    这里给出的答案对我来说只起了一部分作用;很多大文件仍在缓存中。最终的效果(在freenode上以git格式工作数小时后):

    1
    2
    3
    4
    5
    6
    7
    8
    git clone --no-hardlinks file:///SOURCE /tmp/blubb
    cd blubb
    git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --prune-empty --tag-name-filter cat -- --all
    git clone file:///tmp/blubb/ /tmp/blooh
    cd /tmp/blooh
    git reflog expire --expire=now --all
    git repack -ad
    git gc --prune=now

    在以前的解决方案中,存储库大小约为100MB。这个使它降到了1.7MB。也许这对某人有帮助:)

    以下bash脚本自动执行任务:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    !/bin/bash

    if (( $# < 3 ))
    then
        echo"Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
        echo
        echo"Example: $0 /Projects/42.git first/answer/ firstAnswer"
        exit 1
    fi


    clone=/tmp/${3}Clone
    newN=/tmp/${3}

    git clone --no-hardlinks file://$1 ${clone}
    cd ${clone}

    git filter-branch --subdirectory-filter $2  --prune-empty --tag-name-filter cat -- --all

    git clone file://${clone} ${newN}
    cd ${newN}

    git reflog expire --expire=now --all
    git repack -ad
    git gc --prune=now

    这不再是那么复杂了,你只需在你的复制品上使用git filter branch命令,剔除你不想要的子目录,然后推到新的远程。

    1
    2
    git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
    git push <MY_NEW_REMOTE_URL> -f .


    更新:Git子树模块非常有用,以至于Git团队将其放入核心,并使其成为git subtree。请参见此处:将子目录分离(移动)到单独的Git存储库中

    Git子树可能对此有用

    http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt(已弃用)

    http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/


    下面是对CoolAJ86的"The Easy Way&Trade";答案的一个小修改,以便将多个子文件夹(例如,sub1sub2拆分为一个新的git存储库。

    简易方式和交易;(多个子文件夹)

  • 准备旧回购

    1
    2
    3
    4
    pushd <big-repo>
    git filter-branch --tree-filter"mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd

    注意:不能包含前导或尾随字符。例如,名为subproject的文件夹必须作为subproject传递,而不是作为./subproject/传递。

    Windows用户注意:当文件夹深度大于1时,必须有*nix样式的文件夹分隔符(/)。例如,名为path1\path2\subproject的文件夹必须作为path1/path2/subproject传递。此外,不要使用mv命令,而要使用move

    最后一点:与基础答案的独特和巨大区别是脚本"git filter-branch...的第二行"

  • 创建新回购

    1
    2
    3
    4
    5
    mkdir <new-repo>
    pushd <new-repo>

    git init
    git pull </path/to/big-repo> <name-of-new-branch>
  • 将新回购协议链接到Github或任何地方

    1
    2
    git remote add origin <[email protected]:my-user/new-repo.git>
    git push origin -u master
  • 清除(如果需要)

    1
    2
    3
    4
    popd # get out of <new-repo>
    pushd <big-repo>

    git rm -rf <name-of-folder>

    注意:这会将所有的历史引用保留在存储库中。如果您确实担心已提交密码或需要减小.git文件夹的文件大小,请参阅原始答案中的附录。


  • 原始问题希望xyz/abc/(*文件)成为abc/abc/(*文件)。在为自己的代码实现了接受的答案之后,我注意到它实际上将xyz/abc/(*文件)更改为abc/(*文件)。过滤器分支手册页甚至说,

    The result will contain that directory (and only that) as its project root."

    换句话说,它将顶级文件夹"向上"提升一级。这是一个重要的区别,因为,例如,在我的历史中,我重命名了一个顶级文件夹。通过将文件夹"向上"提升一个级别,Git在执行重命名的提交时会失去连续性。

    I lost contiuity after filter-branch

    我对这个问题的答案是复制两份存储库,然后手动删除每个存储库中要保存的文件夹。手册页支持我这样做:

    [...] avoid using [this command] if a simple single commit would suffice to fix your problem


    为了补充保罗的答案,我发现为了最终恢复空间,我必须将头部推到一个干净的存储库中,这样可以缩小.git/objects/pack目录的大小。

    1
    2
    3
    $ mkdir ...ABC.git
    $ cd ...ABC.git
    $ git init --bare

    在GC修剪之后,还要执行以下操作:

    1
    $ git push ...ABC.git HEAD

    然后你可以做

    1
    $ git clone ...ABC.git

    abc/.git的大小减小了

    实际上,推送清理存储库不需要一些耗时的步骤(例如g i t-gc),即:

    1
    2
    3
    4
    $ git clone --no-hardlinks /XYZ /ABC
    $ git filter-branch --subdirectory-filter ABC HEAD
    $ git reset --hard
    $ git push ...ABC.git HEAD

    正确的方法如下:

    git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

    Github现在甚至有关于此类案例的小文章。

    但一定要先将原始repo克隆到单独的目录(因为它会删除所有文件和其他目录,您可能需要使用它们)。

    所以你的算法应该是:

  • 将远程报告克隆到另一个目录
  • 使用git filter-branch只在某些子目录下保留文件,将其推送到新的远程目录。
  • 创建commit以从原始远程repo中删除此子目录

  • 似乎大多数(全部?)这里的答案依赖于某种形式的git filter-branch --subdirectory-filter及其ILK。但在某些情况下,这可能"大多数时候"有效,例如重命名文件夹时,例如:

    1
    2
    3
    4
    5
     ABC/
        /move_this_dir # did some work here, then renamed it to

    ABC/
        /move_this_dir_renamed

    如果使用普通的git过滤器样式提取"move-me-rename",则会丢失最初移动该目录(ref)时从后面发生的文件更改历史记录。

    因此,似乎真正保留所有更改历史记录(如果您的情况是这样的话)的唯一方法,实质上是复制存储库(创建一个新的repo,将其设置为原点),然后对其他所有内容进行核处理,并将子目录重命名为父目录,如下所示:

  • 本地克隆多模块项目
  • 分支机构-检查那里有什么:git branch -a
  • 对要包含在拆分中的每个分支进行签出,以便在工作站上获得本地副本:git checkout --track origin/branchABC
  • 在新目录中复制:cp -r oldmultimod simple
  • 进入新项目副本:cd simple
  • 删除此项目中不需要的其他模块:
  • git rm otherModule1 other2 other3
  • 现在只保留目标模块的子目录
  • 去掉模块子目录,使模块根目录成为新的项目根目录。
  • git mv moduleSubdir1/* .
  • 删除旧分区:rmdir moduleSubdir1
  • 检查任何一点的变化:git status
  • 创建新的git repo并复制其URL以将此项目指向其中:
  • git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
  • 确认这是好的:git remote -v
  • 将更改推到远程回购:git push
  • 去远程回购那里检查一下
  • 对其他需要的分支重复:git checkout branch2
  • 这遵循Github文档"将子文件夹拆分为新存储库"的步骤6-11,将模块推送到新的repo。

    这不会在.git文件夹中为您节省任何空间,但它会保留这些文件的所有更改历史记录,甚至在重命名时也是如此。如果没有"大量"的历史损失等等,这可能不值得,但至少保证你不会失去更老的承诺!


    我确实遇到过这个问题,但是所有基于Git过滤分支的标准解决方案都非常慢。如果你有一个小的存储库,那么这可能不是问题,它是为我准备的。我编写了另一个基于libgit2的git过滤程序,第一步是为主存储库的每个过滤创建分支,然后将这些分支作为下一步推送到清理存储库。在我的存储库(500MB 100000提交)上,标准的Git过滤器分支方法花费了几天时间。我的程序需要几分钟来做同样的过滤。

    它有一个极好的名字,Git_过滤器,住在这里:

    https://github.com/slobaby/git_过滤器

    在吉瑟布上。

    我希望它对某人有用。


    值得一提的是,下面是如何在Windows计算机上使用GitHub。假设您在C:\dir1中有一个克隆的repo。目录结构如下:C:\dir1\dir2\dir3dir3目录是我想成为一个新的独立回购的目录。

    github:

  • 创建新的存储库:MyTeam/mynewrepo
  • BASH提示:

  • $ cd c:/Dir1
  • $ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD返回:Ref 'refs/heads/master' was rewritten(fyi:dir2/dir3区分大小写。)

  • $ git remote add some_name [email protected]:MyTeam/mynewrepo.gitgit remote add origin etc。"不工作",回答"remote origin already exists"

  • $ git push --progress some_name master


  • 使用此筛选命令删除子目录,同时保留标记和分支:

    1
    2
    3
    git filter-branch --index-filter \
    "git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
    --tag-name-filter cat -- --all


    正如我上面提到的,我不得不使用相反的解决方案(删除所有不涉及我的dir/subdir/targetdir的提交),这似乎可以很好地删除大约95%的提交(根据需要)。然而,还有两个小问题。

    首先,filter-branch做了一个非常出色的工作,删除了引入或修改代码的提交,但显然合并提交在Gitiverse中的位置之下。

    • 截图:合并疯狂!

    这是一个表面的问题,我可能可以忍受(他说……慢慢后退,避开眼睛)。

    第二,剩下的几个承诺几乎都是重复的!我似乎已经获得了第二个多余的时间表,它跨越了项目的整个历史。有趣的是(您可以从下面的图片中看到),我的三个本地分支并不都在同一时间线上(这当然是它存在的原因,而且不仅仅是垃圾收集)。

    • screnshot:双双精度,git过滤分支样式

    我唯一能想到的是,其中一个已删除的提交可能是filter-branch实际删除的单个合并提交,并且创建了并行时间线,因为每个现在未合并的链都有自己的提交副本。(耸耸肩,我的塔迪斯呢?)我很确定我能解决这个问题,尽管我真的很想了解它是如何发生的。

    在疯狂并购的情况下,我可能会把它单独留下,因为它已经牢牢地扎根在我的承诺历史中,每当我靠近的时候,它就威胁着我-它似乎并没有真正造成任何非化妆品的问题,因为它在tower.app中相当漂亮。


    更简单的方法

  • 安装git splits。我根据jkeating的解决方案创建了一个git扩展。
  • 将目录拆分为本地分支
    #change into your repo's directory
    cd /path/to/repo
    #checkout the branch
    git checkout XYZ
    #split multiple directories into new branch XYZ
    git splits -b XYZ XY1 XY2

  • 在某处创建一个空回购。我们假设我们在Github上创建了一个名为xyz的空回购,路径为:[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's master branch
    git push origin_xyz XYZ:master

  • 将新创建的远程repo克隆到新的本地目录中
    #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


  • 我推荐Github将子文件夹拆分为新存储库的指南。这些步骤与保罗的答案相似,但我发现它们的说明更容易理解。

    我已经修改了这些指令,以便它们适用于本地存储库,而不是一个托管在GitHub上的存储库。

    Splitting a subfolder out into a new repository

  • Open Git Bash.

  • Change the current working directory to the location where you want to create your new repository.

  • Clone the repository that contains the subfolder.

  • 1
    git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
  • Change the current working directory to your cloned repository.
  • 1
    cd REPOSITORY-NAME
  • To filter out the subfolder from the rest of the files in the repository, run git filter-branch, supplying this information:

    • FOLDER-NAME: The folder within your project that you'd like to create a separate repository from.

      • Tip: Windows users should use / to delimit folders.
    • BRANCH-NAME: The default branch for your current project, for example, master or gh-pages.
  • 1
    2
    3
    4
    git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME
    # Filter the specified branch in your directory and remove empty commits
    Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
    Ref 'refs/heads/BRANCH-NAME' was rewritten


    在https://github.com/vangorra/git_split上查看git_split项目

    把git目录转换成它们自己的存储库。没有子树有趣的事。此脚本将获取Git存储库中的一个现有目录,并将该目录转换为它自己的独立存储库。在此过程中,它将复制您提供的目录的整个更改历史记录。

    1
    2
    3
    4
    5
    ./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
            src_repo  - The source repo to pull from.
            src_branch - The branch of the source repo to pull from. (usually master)
            relative_dir_path   - Relative path of the directory in the source repo to split.
            dest_repo - The repo to push to.


    在垃圾收集之前,您可能需要"git reflog expire--expire=now--all"之类的东西来实际清理文件。git filter分支只删除历史记录中的引用,但不删除保存数据的reflog条目。当然,先测试一下。

    在这样做时,我的磁盘使用率急剧下降,尽管我的初始条件有所不同。也许——子目录过滤器否定了这个需求,但我对此表示怀疑。


    我确信Git子树是非常好的,但是我想要移动的Git托管代码的子目录都在Eclipse中。所以如果你用的是egit,那就很容易了。拿着你想要移动的项目,团队->断开它,然后团队->共享到新的位置。默认情况下,它将尝试使用旧的回购位置,但您可以取消选中"使用现有选择"并选择新位置来移动它。万岁。


    把这个放到你的Gitconfig中:

    1
    reduce-to-subfolder = !sh -c 'git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter cookbooks/unicorn HEAD && git reset --hard && git for-each-ref refs/original/ | cut -f 2 | xargs -n 1 git update-ref -d && git reflog expire --expire=now --all && git gc --aggressive --prune=now && git remote rm origin'

    我找到了非常直接的解决方案,其思想是复制存储库,然后删除不必要的部分。这就是它的工作原理:

    1)克隆要拆分的存储库

    1
    git clone [email protected]:testrepo/test.git

    2)移动到Git文件夹

    1
    cd test/

    2)删除不必要的文件夹并提交

    1
    2
    3
    4
    rm -r ABC/
    git add .
    enter code here
    git commit -m 'Remove ABC'

    3)用bfg删除不必要的文件夹表单历史记录

    1
    2
    3
    4
    cd ..
    java -jar bfg.jar --delete-folders"{ABC}" test
    cd test/
    git reflog expire --expire=now --all && git gc --prune=now --aggressive

    for multiply folders you can use comma

    1
    java -jar bfg.jar --delete-folders"{ABC1,ABC2}" metric.git

    4)检查历史记录是否不包含您刚删除的文件/文件夹。

    1
    git log --diff-filter=D --summary | grep delete

    5)现在你有了没有ABC的干净仓库,所以把它推到新的起点

    1
    2
    remote add origin [email protected]:username/new_repo
    git push -u origin master

    就是这样。您可以重复这些步骤以获取另一个存储库,

    只需删除xy1、xy2并在步骤3中重命名xyz->abc


    您可以轻松地尝试https://help.github.com/enterprise/2.15/user/articles/spliting-a-subfolder-out-into-a-new-repository/

    这对我有用。我在上述步骤中面临的问题是

  • 在这个命令中,git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAMEBRANCH-NAME是主机

  • 如果由于保护问题提交时最后一步失败,请执行-https://docs.gitlab.com/ee/user/project/protected_branches.html