当前位置:  开发笔记 > 编程语言 > 正文

将(移动)子目录分离到单独的Git存储库中

如何解决《将(移动)子目录分离到单独的Git存储库中》经验,为你挑选了12个好方法。

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

如何在将文件的历史记录保存在子目录中的同时执行此操作?

我想我可以制作一个克隆并删除每个克隆的不需要的部分,但我想这会给我一个完整的树,当检查旧版本等.这可能是可以接受的,但我宁愿能够假装两个存储库没有共享历史记录.

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

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想这样做:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

CoolAJ86.. 1259

Easy Way™

事实证明,这是一个非常普遍和有用的做法,git的霸主使它变得非常容易,但你必须有一个更新版本的git(> = 1.7.11 2012年5月).有关如何安装最新git的信息,请参阅附录.此外,下面的演练中有一个真实的例子.

    准备旧的回购

    pushd 
    git subtree split -P  -b 
    popd
    

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

    Windows用户注意事项:当文件夹深度> 1时,必须具有*nix样式文件夹分隔符(/).例如,名为path1\path2\subprojectMUST 的文件夹必须作为path1/path2/subproject

    创建新的仓库

    mkdir 
    pushd 
    
    git init
    git pull  
    

    将新的回购链接链接到Github或任何地方

    git remote add origin 
    git push origin -u master
    

    如果需要,清理

    popd # get out of 
    pushd 
    
    git rm -rf 
    

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

...

演练

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

这是我在节点中实现JavaScript浏览器模块的项目:

tree ~/Code/node-browser-compat

node-browser-compat
??? ArrayBuffer
??? Audio
??? Blob
??? FormData
??? atob
??? btoa
??? location
??? navigator

我想将一个文件夹拆分btoa成一个单独的git存储库

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

我现在有一个新的分支,btoa-only只有提交btoa,我想创建一个新的存储库.

mkdir ~/Code/btoa/
pushd ~/Code/btoa/
git init
git pull ~/Code/node-browser-compat btoa-only

接下来我在Github或bitbucket上创建一个新的repo,或者其他什么并添加它是origin(顺便说一句,"origin"只是一个约定,不是命令的一部分 - 你可以称之为"远程服务器"或任何你喜欢的)

git remote add origin git@github.com:node-browser-compat/btoa.git
git push origin -u master

愉快的一天!

注意:如果你创建了一个带有回购协议README.md,.gitignore并且LICENSE,你需要先拉:

git pull origin -u master
git push origin -u master

最后,我想从更大的仓库中删除该文件夹

git rm -rf btoa

...

附录

OS X上的最新git

要获取最新版本的git:

brew install git

要获得OS X的酿造:

http://brew.sh

关于Ubuntu的最新git

sudo apt-get update
sudo apt-get install git
git --version

如果这不起作用(你有一个非常旧版本的ubuntu),试试吧

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

如果仍然无效,请尝试

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中删除它们,它只是提交它们不再存在.如果要实际删除历史引用(即您已提交密码),则需要执行以下操作:

git filter-branch --prune-empty --tree-filter 'rm -rf ' HEAD

之后,您可以检查您的文件或文件夹根本不再显示在git历史记录中

git log --  # should show nothing

但是,您无法"删除"删除到github等.如果你尝试过你会得到一个错误,你必须git pull在你能做到之前git push- 然后你就会回到你历史上的所有事情.

因此,如果你想从"origin"中删除历史记录 - 意思是从github,bitbucket等删除它 - 你需要删除repo并重新推送repo的修剪副本.但是等等 - 还有更多! - 如果你真的担心要删除密码或类似的东西,你需要修剪备份(见下文).

使.git

前面提到的删除历史记录命令仍然留下了一堆备份文件 - 因为git非常友好,可以帮助您不会意外毁坏您的回购.它最终将在几天和几个月内删除孤立的文件,但是如果你意识到你不小心删除了你不想要的东西,它会在那里留下一段时间.

因此,如果你真的想要清空垃圾桶以立即减少回购的克隆大小,你必须做所有这些非常奇怪的事情:

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

也就是说,我建议不要执行这些步骤,除非你知道你需要 - 以防万一你修剪了错误的子目录,你知道吗?推送回购时,不应克隆备份文件,它们只是在本地副本中.

信用

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

从git永久删除目录

http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

如何从我的git仓库中删除未引用的blob

如果您已将密码推送到公共存储库,则应更改密码,而不是尝试将其从公共存储库中删除,并希望没有人看到它. (39认同)

`git subtree`仍然是'contrib'文件夹的一部分,默认情况下不会安装在所有发行版上.https://github.com/git/git/blob/master/contrib/subtree/ (13认同)

@krlmlr 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在Ubuntu 13.04上激活 (11认同)

`popd`和`pushd`命令使得这更隐蔽,更难以理解它打算做什么...... (9认同)

此解决方案不保留历史记录. (8认同)

这似乎是用'ABC /`的内容创建一个新的repo,但是新的repo不包含文件夹`ABC /`本身,问题就是这样.你会怎么做? (5认同)

这对我不起作用.当我调用`git pull `时,我收到一个错误:`找不到远程ref `. (4认同)

将拆分子树拉入文件夹:`git subtree add --prefix (4认同)

通过执行`git push -f`强制覆盖,可以在推送到GitHub时覆盖推送错误. (3认同)

清理时,不应该删除用`git subtree`创建的**分支**吗? (3认同)

如果我们有一个以前重命名的子目录呢?我在一个``上尝试了这个,它在某个时间点从`'重命名.当我在``中查看历史记录时,初始提交是重命名文件夹的那个 - 之前的所有历史记录都从`中删除了. (3认同)

这种方法基本上没用 - 如果你遵循它就会丢失除master之外的所有分支,以及可能在这些分支中的所有历史记录. (3认同)

@ rui.araujo:我最终在我的`〜/ bin`目录中创建了一个小的shell脚本,它源于`git-subtree.sh`:https://gist.github.com/6338779.当然这也必须是`chmod + x`-d, (2认同)

有没有办法应用此方法以将**多个子文件夹**拆分为新的git存储库? (2认同)

我终于使用了http://stackoverflow.com/questions/19954485/extract-multiple-directories-using-git-filter-branch/19957874#19957874中给出的解决方案,它对我来说效果很好. (2认同)

据我所知,这个方法(`git subtree`)你会松开分支和标签.使用http://stackoverflow.com/a/359759/300632中描述的`git filter-branch`方法,可以保留/重写分支和标记. (2认同)


Paul.. 1203

更新:这个过程非常普遍,git团队使用新工具使其变得更加简单git subtree.请参见此处:将(移动)子目录分离到单独的Git存储库中


您希望克隆您的存储库,然后使用git filter-branch标记除新回购中所需的子目录之外的所有内容进行垃圾回收.

    要克隆本地存储库:

    git clone /XYZ /ABC
    

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

    现在,让我们保留我们想要重写的有趣分支,然后删除原点以避免在那里推送并确保原始提交不会被原点引用:

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

    或者对于所有远程分支:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    

    现在您可能还想删除与子项目无关的标记; 您也可以稍后再这样做,但您可能需要再次修剪您的仓库.我没有这样做,得到了WARNING: Ref 'refs/tags/v0.1' is unchanged所有标签(因为它们都与子项目无关); 此外,在移除此类标签后,将回收更多空间.显然git filter-branch应该能够重写其他标签,但我无法验证这一点.如果要删除所有标签,请使用git tag -l | xargs git tag -d.

    然后使用filter-branch和reset来排除其他文件,这样就可以对它们进行修剪.我们还添加--tag-name-filter cat --prune-empty删除空提交并重写标记(请注意,这将删除其签名):

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

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

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    

    然后删除备份reflogs,以便可以真正回收空间(尽管现在操作具有破坏性)

    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发现的那样,这使得项目分支和标签包含在新的仓库中.

编辑:合并了以下评论中的各种建议,以确保,例如,存储库实际上是缩小的(以前并非总是如此).



1> CoolAJ86..:
Easy Way™

事实证明,这是一个非常普遍和有用的做法,git的霸主使它变得非常容易,但你必须有一个更新版本的git(> = 1.7.11 2012年5月).有关如何安装最新git的信息,请参阅附录.此外,下面的演练中有一个真实的例子.

    准备旧的回购

    pushd 
    git subtree split -P  -b 
    popd
    

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

    Windows用户注意事项:当文件夹深度> 1时,必须具有*nix样式文件夹分隔符(/).例如,名为path1\path2\subprojectMUST 的文件夹必须作为path1/path2/subproject

    创建新的仓库

    mkdir 
    pushd 
    
    git init
    git pull  
    

    将新的回购链接链接到Github或任何地方

    git remote add origin 
    git push origin -u master
    

    如果需要,清理

    popd # get out of 
    pushd 
    
    git rm -rf 
    

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

...

演练

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

这是我在节点中实现JavaScript浏览器模块的项目:

tree ~/Code/node-browser-compat

node-browser-compat
??? ArrayBuffer
??? Audio
??? Blob
??? FormData
??? atob
??? btoa
??? location
??? navigator

我想将一个文件夹拆分btoa成一个单独的git存储库

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

我现在有一个新的分支,btoa-only只有提交btoa,我想创建一个新的存储库.

mkdir ~/Code/btoa/
pushd ~/Code/btoa/
git init
git pull ~/Code/node-browser-compat btoa-only

接下来我在Github或bitbucket上创建一个新的repo,或者其他什么并添加它是origin(顺便说一句,"origin"只是一个约定,不是命令的一部分 - 你可以称之为"远程服务器"或任何你喜欢的)

git remote add origin git@github.com:node-browser-compat/btoa.git
git push origin -u master

愉快的一天!

注意:如果你创建了一个带有回购协议README.md,.gitignore并且LICENSE,你需要先拉:

git pull origin -u master
git push origin -u master

最后,我想从更大的仓库中删除该文件夹

git rm -rf btoa

...

附录

OS X上的最新git

要获取最新版本的git:

brew install git

要获得OS X的酿造:

http://brew.sh

关于Ubuntu的最新git

sudo apt-get update
sudo apt-get install git
git --version

如果这不起作用(你有一个非常旧版本的ubuntu),试试吧

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

如果仍然无效,请尝试

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中删除它们,它只是提交它们不再存在.如果要实际删除历史引用(即您已提交密码),则需要执行以下操作:

git filter-branch --prune-empty --tree-filter 'rm -rf ' HEAD

之后,您可以检查您的文件或文件夹根本不再显示在git历史记录中

git log --  # should show nothing

但是,您无法"删除"删除到github等.如果你尝试过你会得到一个错误,你必须git pull在你能做到之前git push- 然后你就会回到你历史上的所有事情.

因此,如果你想从"origin"中删除历史记录 - 意思是从github,bitbucket等删除它 - 你需要删除repo并重新推送repo的修剪副本.但是等等 - 还有更多! - 如果你真的担心要删除密码或类似的东西,你需要修剪备份(见下文).

使.git

前面提到的删除历史记录命令仍然留下了一堆备份文件 - 因为git非常友好,可以帮助您不会意外毁坏您的回购.它最终将在几天和几个月内删除孤立的文件,但是如果你意识到你不小心删除了你不想要的东西,它会在那里留下一段时间.

因此,如果你真的想要清空垃圾桶以立即减少回购的克隆大小,你必须做所有这些非常奇怪的事情:

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

也就是说,我建议不要执行这些步骤,除非你知道你需要 - 以防万一你修剪了错误的子目录,你知道吗?推送回购时,不应克隆备份文件,它们只是在本地副本中.

信用

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

从git永久删除目录

http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

如何从我的git仓库中删除未引用的blob


如果您已将密码推送到公共存储库,则应更改密码,而不是尝试将其从公共存储库中删除,并希望没有人看到它.
`git subtree`仍然是'contrib'文件夹的一部分,默认情况下不会安装在所有发行版上.https://github.com/git/git/blob/master/contrib/subtree/
@krlmlr 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在Ubuntu 13.04上激活
`popd`和`pushd`命令使得这更隐蔽,更难以理解它打算做什么......
此解决方案不保留历史记录.
这似乎是用'ABC /`的内容创建一个新的repo,但是新的repo不包含文件夹`ABC /`本身,问题就是这样.你会怎么做?
这对我不起作用.当我调用`git pull `时,我收到一个错误:`找不到远程ref `.
将拆分子树拉入文件夹:`git subtree add --prefix
通过执行`git push -f`强制覆盖,可以在推送到GitHub时覆盖推送错误.
清理时,不应该删除用`git subtree`创建的**分支**吗?
如果我们有一个以前重命名的子目录呢?我在一个``上尝试了这个,它在某个时间点从`'重命名.当我在``中查看历史记录时,初始提交是重命名文件夹的那个 - 之前的所有历史记录都从`中删除了.
这种方法基本上没用 - 如果你遵循它就会丢失除master之外的所有分支,以及可能在这些分支中的所有历史记录.
@ rui.araujo:我最终在我的`〜/ bin`目录中创建了一个小的shell脚本,它源于`git-subtree.sh`:https://gist.github.com/6338779.当然这也必须是`chmod + x`-d,
有没有办法应用此方法以将**多个子文件夹**拆分为新的git存储库?
我终于使用了http://stackoverflow.com/questions/19954485/extract-multiple-directories-using-git-filter-branch/19957874#19957874中给出的解决方案,它对我来说效果很好.
据我所知,这个方法(`git subtree`)你会松开分支和标签.使用http://stackoverflow.com/a/359759/300632中描述的`git filter-branch`方法,可以保留/重写分支和标记.

2> Paul..:

更新:这个过程非常普遍,git团队使用新工具使其变得更加简单git subtree.请参见此处:将(移动)子目录分离到单独的Git存储库中


您希望克隆您的存储库,然后使用git filter-branch标记除新回购中所需的子目录之外的所有内容进行垃圾回收.

    要克隆本地存储库:

    git clone /XYZ /ABC
    

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

    现在,让我们保留我们想要重写的有趣分支,然后删除原点以避免在那里推送并确保原始提交不会被原点引用:

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

    或者对于所有远程分支:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    

    现在您可能还想删除与子项目无关的标记; 您也可以稍后再这样做,但您可能需要再次修剪您的仓库.我没有这样做,得到了WARNING: Ref 'refs/tags/v0.1' is unchanged所有标签(因为它们都与子项目无关); 此外,在移除此类标签后,将回收更多空间.显然git filter-branch应该能够重写其他标签,但我无法验证这一点.如果要删除所有标签,请使用git tag -l | xargs git tag -d.

    然后使用filter-branch和reset来排除其他文件,这样就可以对它们进行修剪.我们还添加--tag-name-filter cat --prune-empty删除空提交并重写标记(请注意,这将删除其签名):

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

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

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    

    然后删除备份reflogs,以便可以真正回收空间(尽管现在操作具有破坏性)

    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发现的那样,这使得项目分支和标签包含在新的仓库中.

编辑:合并了以下评论中的各种建议,以确保,例如,存储库实际上是缩小的(以前并非总是如此).


我建议的另一个步骤是"git remote rm origin".如果我没有弄错的话,这将继续推回原始存储库.
非常好的答案.谢谢!为了真正得到我想要的东西,我在filter-branch命令中添加了" - --all".
附加到`filter-branch`的另一个命令是`--proune-empty`,用于删除now-empty提交.
你为什么需要`--no-hardlinks`?删除一个硬链接不会影响另一个文件.Git对象也是不可变的.只有当您更改所有者/文件权限时,您才需要`--no-hardlinks`.
像保罗一样,我不想在我的新回购中使用项目标签,所以我没有使用` - --all`.我还运行了`git remote rm origin`和`git tag -l | 在`git filter-branch`命令之前的xargs git tag -d`.这使我的`.git`目录从60M缩小到~300K.请注意,我需要运行这两个命令才能减小大小.
如果要重写标记以不引用旧结构,请添加`--tag-name-filter cat`
git手册页推荐,而不是`rm -rf .git/refs/original /`,`git for-each-ref --format ="%(refname)"refs/original/| xargs -n 1 git update-ref -d`; 我猜如果refs没有存储在正确的位置,后者会更强大.此外,我相信'git remote rm origin'也需要缩小回购,否则来自原点的refs将保持引用的对象.@jonp,我认为那是你的问题.最后,为了重写其他分支,必须在cloninng,` - --all`之后用`git branch`手动设置它们,并删除`HEAD`(停止重写其他分支).
我发现git-subtree也很有效,而且更简单.通过此相关问题/答案找到:http://stackoverflow.com/a/1307969/72
这不是创建ABC /而不是ABC/ABC /?

3> pgs..:

Paul的回答创建了一个包含/ ABC的新存储库,但是没有从/ XYZ中删除/ ABC.以下命令将从/ XYZ中删除/ ABC:

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

当然,首先在'clone --no-hardlinks'存储库中测试它,然后使用Paul列出的reset,gc和prune命令进行测试.


使那个`git filter-branch --index-filter"git rm -r -f --cached --ignore-unmatch ABC"--prune-empty HEAD`,它将会更快**.index-filter对索引起作用,而tree-filter必须为每次提交检出并暂存**.
在某些情况下搞乱存储库XYZ的历史是过度的...只是一个简单的"rm -rf ABC; git rm -r ABC; git commit -m'extracted ABC into the repo'"对大多数人来说会更好.
如果你正在使用`--index-filter`方法,你可能还想制作`git rm -q -r -f`,这样每次调用都不会为它删除的每个文件打印一行.
如果多次执行此操作,您可能希望对此命令使用-f(强制),例如,在分离后删除两个目录.否则,您将收到"无法创建新备份".

4> Josh Lee..:

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

    做克隆和过滤器:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    

    删除对旧历史的每个引用."origin"跟踪你的克隆,"original"是filter-branch保存旧东西的地方:

    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    

    即使是现在,你的历史可能会陷入fsck不会触及的包文件中.撕碎它,创建一个新的packfile并删除未使用的对象:

    git repack -ad
    

有一个这样的解释中手册过滤分支.


我认为像'git gc --aggressive --prune = now`这样的想法仍然缺失,不是吗?

5> Simon A. Eug..:

编辑:添加了Bash脚本.

这里给出的答案对我来说只是部分起作用; 缓存中还有很多大文件.什么最终奏效(freenode #git下班后):

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

使用以前的解决方案,存储库大小约为100 MB.这个降低到1.7 MB.也许它有助于某人:)


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

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0   "
    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



6> jeremyjjbrow..:

这不再是那么复杂,您可以在您的repo克隆上使用git filter-branch命令来剔除您不想要的子目录,然后推送到新的远程.

git filter-branch --prune-empty --subdirectory-filter  master
git push  -f .


这就像一个魅力.上面示例中的YOUR_SUBDIR是您要保留的子目录,其他所有内容都将被删除

7> D W..:

更新:git-subtree模块非常有用,git团队将其拉入核心并成功实现git subtree.请参见此处:将(移动)子目录分离到单独的Git存储库中

git-subtree可能对此有用

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/



8> Anthony O...:

这是对CoolAJ86的"The Easy Way™"答案的一个小修改,以便将多个子文件夹(比如说sub1sub2)拆分成一个新的git存储库.

Easy Way™(多个子文件夹)

    准备旧的回购

    pushd 
    git filter-branch --tree-filter "mkdir ; mv   /" HEAD
    git subtree split -P  -b 
    popd
    

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

    Windows用户注意事项:当文件夹深度> 1时,必须具有*nix样式文件夹分隔符(/).例如,名为path1\path2\subprojectMUST 的文件夹必须作为path1/path2/subproject.而且不要使用mv命令move.

    最后的注释:基本答案的独特和巨大差异是脚本的第二行" git filter-branch..."

    创建新的仓库

    mkdir 
    pushd 
    
    git init
    git pull  
    

    将新的回购链接链接到Github或任何地方

    git remote add origin 
    git push origin -u master
    

    如果需要,清理

    popd # get out of 
    pushd 
    
    git rm -rf 
    

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



9> MM...:

原始问题想要XYZ/ABC /(*文件)成为ABC/ABC /(*文件).在为我自己的代码实现接受的答案后,我注意到它实际上将XYZ/ABC /(*文件)更改为ABC /(*文件).filter-branch手册页甚至说,

结果将包含该目录(并且仅包含该目录)作为其项目根目录."

换句话说,它将顶级文件夹"升级"一级.这是一个重要的区别,因为,例如,在我的历史中,我已经重命名了一个顶级文件夹.通过将文件夹"提升"到一个级别,git在我进行重命名的提交时失去连续性.

过滤器分支后我失去了连续性

我对问题的回答是制作2个存储库副本并手动删除要保留在每个存储库中的文件夹.该手册页支持我:

[...]如果简单的单一提交足以解决您的问题,请避免使用[此命令]


适用于Mac的塔.我很喜欢.它几乎值得切换到Mac本身.
是的,虽然在我的情况下,我的子文件夹`targetdir`在某些时候已被**重命名**并且`git filter-branch`只是简单地称它为一天,删除重命名之前所做的所有提交!令人震惊的是,考虑到Git在跟踪这些事情以及个别内容块的迁移方面有多么娴熟!

10> 小智..:

为了增加Paul的答案,我发现要最终恢复空间,我必须将HEAD推送到一个干净的存储库,并减少.git/objects/pack目录的大小.

$ mkdir ...ABC.git
$ cd ...ABC.git
$ git init --bare

gc修剪后,也做:

$ git push ...ABC.git HEAD

那你可以做

$ git clone ...ABC.git

并且减少了ABC/.git的大小

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

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



11> 小智..:

现在正确的方法如下:

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

GitHub现在甚至有关于此类案件的小文章.

但请务必先将原始仓库克隆到单独的目录中(因为它会删除所有文件和其他目录,并且您可能需要使用它们).

所以你的算法应该是:

    克隆远程仓库到另一个目录

    git filter-branch在一些子目录下使用只剩下的文件,推送到新的远程

    create commit从原始远程仓库中删除此子目录



12> rogerdpack..:

似乎这里的大多数(所有?)答案都依赖于某种形式的git filter-branch --subdirectory-filter和类似的.这可能"大多数时间"工作但是对于某些情况,例如重命名文件夹的情况,例如:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

如果你做一个普通的Git过滤器的风格,以提取"move_me_renamed"你将失去的文件变化的历史,从后发生时它最初move_this_dir(REF).

因此,看来真正保留所有变更历史的唯一方法(如果你的是这样的情况),实质上是复制存储库(创建一个新的存储库,设置它作为原点),然后核对其他一切并将子目录重命名为父目录,如下所示:

    在本地克隆多模块项目

    分支 - 检查那里有什么: git branch -a

    对每个分支进行检查以包含在拆分中以获取工作站上的本地副本: git checkout --track origin/branchABC

    在新目录中制作副本: cp -r oldmultimod simple

    进入新项目副本: cd simple

    摆脱这个项目中不需要的其他模块:

    git rm otherModule1 other2 other3

    现在只剩下目标模块的子目录

    删除模块子目录,以便模块根目录成为新的项目根目录

    git mv moduleSubdir1/* .

    删除relic子目录: 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以将模块推送到新的存储库.

这不会为.git文件夹中的任何空间节省任何空间,但它会保留这些文件的所有更改历史记录,甚至是重命名文件.如果没有"很多"历史丢失等等,这可能不值得.但至少你保证不会丢失旧的提交!

推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有