Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将子目录分离(移动)到单独的Git存储库中_Git_Git Subtree_Git Filter Branch - Fatal编程技术网

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

将子目录分离(移动)到单独的Git存储库中,git,git-subtree,git-filter-branch,Git,Git Subtree,Git Filter Branch,我有一个包含许多子目录的存储库。现在我发现其中一个子目录与另一个子目录无关,应该分离到单独的存储库 mkdir ~/btoa/ && cd ~/btoa/ git init git pull ~/node-browser-compat btoa-only 如何在保持子目录中文件的历史记录的同时执行此操作 我想我可以制作一个克隆并删除每个克隆中不需要的部分,但我想这会在签出较旧版本等时为我提供完整的树。这可能是可以接受的,但我更希望能够假装这两个存储库没有共享的历史 我只想说清楚

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

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only
如何在保持子目录中文件的历史记录的同时执行此操作

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

我只想说清楚,我有以下结构:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/
但我希望这样:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/
更新:这个过程非常常见,git团队使用一个新工具git subtree使它变得更加简单。请看这里:

您希望克隆存储库,然后使用git filter branch标记除您希望在新repo中被垃圾收集的子目录之外的所有内容

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

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
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
现在,您可能还希望删除与子项目无关的标记;你以后也可以这样做,但你可能需要再次削减回购协议。我没有这样做,并得到一个警告:Ref'refs/tags/v0.1'对于所有标签都是不变的,因为它们都与子项目无关;此外,移除此类标签后,将回收更多空间。显然git过滤器分支应该能够重写其他标记,但我无法验证这一点。如果要删除所有标记,请使用git tag-l | xargs git tag-d

然后使用过滤器分支和重置排除其他文件,以便可以修剪它们。我们还将添加-tag name filter cat-prune empty以删除空提交并重写标记注意,这将必须去掉它们的签名:

git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
或者,仅重写头部分支并忽略标记和其他分支:

cd /ABC
for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
git remote rm origin
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
然后删除备份重日志,以便真正回收空间,尽管现在操作具有破坏性

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过滤器分支确实应该添加参数--all。是的,那真的是——空间——全部。这需要是命令的最后一个参数。正如Matli所发现的那样,这会将项目分支和标记保留在新回购协议中

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

创建一个包含/ABC的新存储库,但不会从/XYZ中删除/ABC。以下命令将从/XYZ中删除/ABC:

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

当然,首先在“clone-no hardlinks”存储库中测试它,然后使用reset、gc和prune命令进行测试。

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

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

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

i、 e

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

$ git push ...ABC.git HEAD 那你就可以了

$ git clone ...ABC.git 并且ABC/.git的大小减小了

实际上,push to clean存储库不需要一些耗时的步骤,例如git gc,例如:

$ git clone --no-hardlinks /XYZ /ABC $ git filter-branch --subdirectory-filter ABC HEAD $ git reset --hard $ git push ...ABC.git HEAD
我发现,为了正确地从新存储库中删除旧的历史记录,您必须在过滤器分支步骤之后再做一些工作

执行克隆和筛选操作:

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不会触及的打包文件中。将其撕成碎片,创建新的打包文件并删除未使用的对象:

git repack -ad
更新中有一个问题:git子树模块非常有用,git团队将其拉入核心并制作成git子树。请看这里:

git子树在这方面可能很有用

不赞成


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

cd /ABC
for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
git remote rm origin
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
git过滤器分支-索引过滤器\ 吉特r m-r-f-cached-ignore unmatch DIR-prune empty\ -标签名称过滤器类别--全部 编辑:添加了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.7MB。也许这对某人有帮助:

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

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

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

Github:

创建新存储库:MyTeam/mynewrepo Bash提示符:

$cd c:/Dir1 $git筛选器分支-修剪空-子目录筛选器dir2/dir3头 返回:Ref“Ref/heads/master”已重写仅供参考:dir2/dir3区分大小写

$git remote添加一些\u名称git@github.com:MyTeam/mynewrepo.git git远程添加源等不起作用,返回的远程源已存在

$git push-对某个名称主控程序进行升级


原始问题希望XYZ/ABC/*文件变成ABC/ABC/*文件。在为我自己的代码实现了公认的答案之后,我注意到它实际上将XYZ/ABC/*文件更改为ABC/*文件。filter branch手册页甚至说

结果将包含该目录,并且仅包含该目录作为其项目根

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

我对这个问题的回答是制作存储库的两个副本,并手动删除每个副本中要保留的文件夹。手册页用以下内容支持我:

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


将其放入gitconfig:

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'
正如我所说,我必须使用反向解决方案删除所有不涉及我的dir/subdir/targetdir的提交,这似乎可以很好地根据需要删除大约95%的提交。然而,还有两个小问题

首先,filter branch在删除引入或修改代码的提交方面做得非常出色,但显然,合并提交位于其在Gitiverse中的站点之下

“这是一个表面问题,我可能可以接受,”他说,“眼睛移开,慢慢后退。”

其次,剩下的几个提交几乎都是重复的!我似乎已经获得了第二个多余的时间表,它几乎涵盖了整个项目的历史。从下面的图片中你可以看到一件有趣的事情,那就是我的三个本地分支不都在同一个时间线上,这就是为什么它存在,而不仅仅是垃圾收集

我唯一能想象的是,其中一个已删除的提交可能是筛选器分支实际删除的单个合并提交,它创建了并行时间线,因为每个现在未合并的链都有自己的提交副本。耸耸肩我的塔迪斯呢?我很确定我能解决这个问题,尽管我很想知道它是怎么发生的

在疯狂的mergefest-O-RAMA的案例中,我可能会把它放在一边,因为它已经牢牢地扎根在我的犯罪历史中,每当我走近它时,它都会对我构成威胁-,它似乎实际上不会造成任何非化妆品问题,因为它在Tower.app中非常漂亮。

简单的方法™ 事实证明,这是一种非常普遍和有用的做法,Git的霸主让它变得非常简单,但您必须拥有更新版本的Git>=1.7.11 2012年5月。有关如何安装最新Git的信息,请参见附录。此外,下面的演练中还有一个真实的示例

准备旧回购协议

 cd <big-repo>
 git subtree split -P <name-of-folder> -b <name-of-new-branch>
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
将新回购链接到GitHub或任何地方

 git remote add origin <git@github.com:user/new-repo.git>
 git push -u origin master
git remote add origin <git@github.com:my-user/new-repo.git>
git push origin -u master
我想将单个文件夹btoa拆分为一个单独的Git存储库

cd ~/node-browser-compat/
git subtree split -P btoa -b btoa-only
我现在有一个新的分支,仅btoa,它只有btoa的提交,我想创建一个新的存储库

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only
接下来,我在GitHub或Bitbucket上创建一个新的回购协议,或者其他任何东西,并将其添加为源

快乐的一天

注意:如果您使用README.md、.gitignore和许可证创建了一个回购,则需要首先拉取:

git pull origin master
git push origin master
最后,我想从更大的回购中删除该文件夹

git rm -rf btoa
附录 macOS上的最新Git 要获取Git的最新版本,请使用:

Ubuntu上的最新Git 如果这不起作用,你有一个非常旧版本的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
谢谢你的评论

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

git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD
话虽如此,我还是要 ecommend不执行这些步骤,除非您知道您需要执行这些步骤-以防您删除了错误的子目录,您知道吗?当您推送repo时,备份文件不应该被克隆,它们只会在您的本地副本中

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

它的名字叫git_filter,生活在这里:

在GitHub上


我希望它对某些人有用。

这已经不再那么复杂了,您可以在repo的克隆上使用命令来剔除不需要的子目录,然后推送到新的远程目录

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

现在正确的方法是:

git筛选器分支-删除空-子目录筛选器文件夹\u名称[第一个\u分支][另一个\u分支]

GitHub现在甚至对此类案例也有了更多的了解

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

因此,您的算法应该是:

将远程repo克隆到另一个目录 使用git过滤器分支只在某些子目录下留下文件,推送到新的远程目录 创建提交以从原始远程repo中删除此子目录 更简单的方法 安装我创建它作为git扩展,基于。 将目录拆分为本地分支 更改为您的回购目录 cd/path/to/repo 结帐 git签出XYZ 将多个目录拆分为新的分支XYZ git拆分-b XYZ XY1 XY2

在某处创建一个空的回购协议。我们假设在GitHub上创建了一个名为xyz的空repo,其路径为:git@github.com:simplip/xyz.git

推动新的回购协议。 为空repo添加一个新的远程源,这样我们就可以在GitHub上推送到空repo git远程添加源代码\u xyzgit@github.com:simplip/xyz.git 将分支机构推送到空回购主分支机构 git推送原点_xyz xyz:master

将新创建的远程repo克隆到新的本地目录中 将当前目录从旧的repo中更改出来 cd/path/to/where/you/want/the/new/local/repo 克隆您刚刚推送到的远程回购 git克隆git@github.com:simplip/xyz.git


这里是对的一个小修改,以便将多个子文件夹(比如说Sub1和sub2)拆分为一个新的git存储库

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only
捷径™ 多个子文件夹 准备旧回购协议

 cd <big-repo>
 git subtree split -P <name-of-folder> -b <name-of-new-branch>
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
将新回购链接到Github或任何地方

 git remote add origin <git@github.com:user/new-repo.git>
 git push -u origin master
git remote add origin <git@github.com:my-user/new-repo.git>
git push origin -u master
清理,如果需要的话

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


查看git_split项目

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

./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子树非常好,但是我想移动的git托管代码的子目录都在eclipse中。 因此,如果您正在使用egit,它非常简单。 将要移动的项目放入团队->断开连接,然后团队->将其共享到新位置。默认情况下,它将尝试使用旧回购位置,但您可以取消选中“使用现有”选项并选择新位置来移动它。
所有人都在欢呼。

看来,大多数人都在欢呼?这里的答案之一依赖于某种形式的git过滤器分支-子目录过滤器及其同类产品。但是,在某些情况下,这可能大多数情况下都有效,例如重命名文件夹时,例如:

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

ABC/
    /move_this_dir_renamed
如果您使用普通的git过滤器样式提取move\u this\u dir\u重命名,您将丢失最初移动\u this\u dir\u时发生的文件更改历史记录

因此,如果是这样的情况,那么要真正保留所有更改历史记录,唯一的方法就是复制存储库,创建一个新的repo,将其设置为源,然后取消所有其他操作,并将子目录重命名为父目录,如下所示:

本地克隆多模块项目 分支-检查有什么:git分支-a 对要包含在拆分中的每个分支进行签出,以在工作站上获取本地副本:git checkout-track origin/branchABC 在一个新目录中创建一个副本:cp-r oldmultimod simple 进入新项目副本:cd simple 去掉其他在中不需要的模块 本项目: git rm其他模块1其他2其他3 现在只剩下目标模块的subdir 去掉模块子目录,使模块根目录成为新的项目根目录 git mv模块subdir1/*。 删除relic subdir:rmdir模块subdir1 检查任意点的更改:git状态 创建新的git repo并复制其URL以将此项目指向其中: git远程设置url源http://mygithost:8080/git/our-分模块回购 验证这是否正确:git remote-v 将更改推送到远程repo:git Push 去远程回购,检查是否都在那里 对任何其他需要的分支重复此操作:git checkout branch2 这遵循步骤6-11,将模块推送到新的回购协议

这不会在.git文件夹中为您节省任何空间,但它会保留这些文件的所有更改历史记录,即使在重命名过程中也是如此。如果没有太多的历史损失,这可能不值得,等等。但至少你保证不会丢失旧的提交

我推荐。步骤与类似,但我发现它们的说明更容易理解

我修改了说明,使其适用于本地存储库,而不是GitHub上的存储库

打开gitbash

将当前工作目录更改为要创建新存储库的位置

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only
克隆包含子文件夹的存储库

git克隆旧存储库文件夹新存储库文件夹 将当前工作目录更改为克隆的存储库。 cd存储库名称 要从存储库中的其余文件中筛选出子文件夹,请运行git filter branch,并提供以下信息: FOLDER-NAME:项目中要从中创建单独存储库的文件夹。 提示:Windows用户应使用/来分隔文件夹。 BRANCH-NAME:当前项目的默认分支,例如母版页或gh页。 git筛选器分支-删除空-子目录筛选器文件夹-NAME分支-NAME 筛选目录中的指定分支并删除空提交 重写48dc599c80e20527ed902928085e7861e6b3cbe6 89/89 Ref“Ref/heads/BRANCH-NAME”已重写 你可以很容易地尝试

这对我有用。我在上述步骤中遇到的问题如下

在此命令中,git filter branch-prune empty-subdirectory filter FOLDER-NAME branch-NAME 分支名称是master

如果由于保护问题在提交时最后一步失败,请遵循-


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

1克隆要拆分的存储库

git clone git@git.thehost.io:testrepo/test.git
2移动到git文件夹

cd test/
2删除不必要的文件夹并提交

rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'
3使用删除历史记录中不必要的文件夹

对于多个文件夹,可以使用逗号

java -jar bfg.jar --delete-folders "{ABC1,ABC2}" metric.git
4检查历史记录是否不包含刚删除的文件/文件夹

git log --diff-filter=D --summary | grep delete
5现在,您有了没有ABC的干净存储库, 所以只要把它推到新的原点

remote add origin git@github.com:username/new_repo
git push -u origin master
就这样。您可以重复这些步骤以获取另一个存储库

当使用更新版本的git 2.22+maybe?运行git filter branch时,只需删除XY1、XY2并在第3步重命名XYZ->ABC即可,它表示要使用这个新工具。这个工具确实简化了我的工作

过滤与过滤回购 从原始问题创建XYZ报告的命令:

# create local clone of original repo in directory XYZ
tmp $ git clone git@github.com:user/original.git XYZ

# switch to working in XYZ
tmp $ cd XYZ

# keep subdirectories XY1 and XY2 (dropping ABC)
XYZ $ git filter-repo --path XY1 --path XY2

# note: original remote origin was dropped
# (protecting against accidental pushes overwriting original repo data)

# XYZ $ ls -1
# XY1
# XY2

# XYZ $ git log --oneline
# last commit modifying ./XY1 or ./XY2
# first commit modifying ./XY1 or ./XY2

# point at new hosted, dedicated repo
XYZ $ git remote add origin git@github.com:user/XYZ.git

# push (and track) remote master
XYZ $ git push -u origin master
假设: *推送之前,远程XYZ repo是新的且为空的

过滤和移动 在我的例子中,我还想移动几个目录以获得更一致的结构。最初,我运行了简单的filter repo命令,然后使用git mv dir进行重命名,但我发现使用-path重命名选项可以获得更好的历史记录。我现在看到的是去年在GitHub UI中看到的,它与原始回购中的修改时间相匹配,而不是5小时前在新回购中移动的文件上看到的上次修改

而不是

git filter-repo --path XY1 --path XY2 --path inconsistent
git mv inconsistent XY3  # which updates last modification time
我最终跑了

git filter-repo --path XY1 --path XY2 --path inconsistent --path-rename inconsistent:XY3
笔记: 我认为该报告很好地解释了创建另一个回购过滤工具背后的原因。 我最初尝试在原始存储库中创建与目标repo名称匹配的子目录,然后使用git filter repo-subdirectory filter dir匹配新repo名称进行过滤。该命令正确地将该子目录转换为复制的本地repo的根目录,但它也只生成创建该子目录所需的三次提交的历史记录。我没有意识到-路径可以多次指定;因此,无需在源repo中创建子目录。由于在我注意到无法继续历史记录时,有人已经提交了源repo,所以我只是在clone命令之后使用git reset commit before subdir move-hard,并在filter repo命令中添加了-force,以使其在稍微修改的本地克隆上运行。 git克隆。。。 git复位头~7-在误击前硬回滚 E git过滤器报告-强制告知过滤器回购预计会发生变更 由于我不知道git的扩展模式,所以在安装过程中遇到了麻烦,但最终我将其克隆并符号链接到$git-exec path: ln-s~/github/newren/git filter repo/git filter repo$git-exec路径

让git过滤器分支-索引过滤器git rm-r-f-cached-ignore unmatch ABC-prune空头,速度会快得多。索引过滤器对索引起作用,而树过滤器必须为每次提交签出和暂存所有内容。在某些情况下,弄乱存储库XYZ的历史记录是一种过激的行为。。。只是一个简单的rm-rf ABC;吉特rm-r ABC;git commit-m'extracted ABC into to it own repo'对大多数人来说效果更好。如果您不止一次地执行此命令,您可能希望在此命令上使用-f force,例如,在两个目录分离后删除它们。否则,您将无法创建新的备份。如果您正在使用-index筛选方法,您可能还希望使用git rm-q-r-f,这样每次调用都不会为它删除的每个文件打印一行。我建议编辑Paul的答案,只是因为Paul的答案非常彻底。为什么需要-无硬链接?删除一个硬链接不会影响其他文件。Git对象也是不可变的。只有在更改所有者/文件权限时,才需要-no-hardlinks。如果要重写标记以不引用旧结构,请添加-tag name filter catLike Paul,我不希望在新的repo中使用项目标记,因此我没有使用--all。在git filter branch命令之前,我还运行了git remote rm origin和git tag-l | xargs git tag-d。这将我的.git目录从60M缩减到~300K。请注意,我需要运行这两个命令来减小大小。git手册页建议,每个ref-format=%refname refs/original/| xargs-n 1 git update ref-d;如果ref没有存储在正确的位置,我想后者会更健壮。此外,我认为还需要“git remote rm origin”来收缩repo,否则origin的ref将保持对象被引用@乔普,我想这是你的问题。最后,要重写其他分支,必须在关闭后使用git branch手动设置它们,-all并删除停止重写其他分支的HEAD。这不是创建了ABC/而不是ABC/ABC/?我认为像git-gc-aggressive-prune=这样的想法仍然不存在,不是吗?@Albert repack命令解决了这个问题,而且不会有任何松散的对象。是的,git gc-aggressive-prune=现在减少了许多简单而优雅的新对象。谢谢在这一切之后,我仍然得到了和以前一样的错误。致命:存储在.git/objects/pack/pack-yyyyyyyyyy.pack中的打包对象xxxxxx与该图形的样式类似。我可以问一下你用的是什么工具吗。我真的很喜欢。是的,不过在我的例子中,我的子文件夹targetdir在某个时候被重命名了,git filter branch干脆把它叫停了,删除了重命名之前的所有提交!令人震惊的是,考虑到Git在跟踪这些东西甚至迁移单个内容块方面是多么熟练!哦,还有,如果有人发现自己在同一条船上,这是我使用的命令。不要忘记git rm需要多个参数,因此没有理由为每个文件/文件夹运行它:BYEBYE=dir/subdir2 dir2 file1 dir/file2;git filter branch-f-index filter git rm-q-r-f-cached-ignore unmatch$BYEBYE-prune empty——allgit子树现在是git的一部分,尽管它位于contrib树中,所以默认情况下并不总是安装。我知道它是通过自制git公式安装的,但没有其手册页。因此,apenwarr将其版本称为过时版本。git子树仍然是“contrib”文件夹的一部分,默认情况下不会安装在所有发行版上@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命令使其变得相当含蓄,更难理解它的意图……这对于git筛选器分支来说是微不足道的,请参见下面我的答案。@jeremyjjbrown是对的。这不再困难,但在谷歌上很难找到正确的答案,因为所有的旧答案都支配着搜索结果。这很有魅力。上面示例中的_子目录是您要保留的子目录,其他所有内容都将根据您的评论进行删除更新。这并不能回答问题。从文档中,它表示结果将包含该目录,并且只有该目录作为其项目根。事实上,这就是你将得到的,也就是说,原来的项目结构没有得到保留。@NicBright你能说明你的问题吗
对于问题中的XYZ和ABC,为了说明问题出在哪里?@jeremyjjbrown是否可以重用克隆的回购协议而不使用新的回购协议,也就是说,我的问题是,与简单方法相比,这种方法的一个优点是远程已为新的回购协议设置,因此您可以立即进行子树添加。事实上,即使没有git splitsProps向AndrewD发布此解决方案,这种方式对我来说似乎更容易。如果OSX对其他人有用的话,我已经用他的回购协议让它在OSX上运行了。这对我有用,只需稍加修改。因为我的sub1和sub2文件夹在初始版本中不存在,所以我必须修改我的树过滤器脚本,如下所示:mkdir;如果[-d sub1];然后mv/;菲。对于第二个filter branch命令,我将其替换为,省略了filter branch的创建,并在filter branch之后包含-f,以覆盖现有备份的警告。如果git中的历史记录中有任何子目录发生了更改,则此操作无效。“这怎么能解决?”尼特拉斯看到罗杰德帕克的答案。在阅读并吸收了这些其他答案中的所有信息后,我花了一段时间才找到它。子树的精妙之处在于,你的子目录的历史记录是随便便而来的。如果你不需要历史记录,那么你最简单的方法就是这样。cat在这里是什么?很好的帖子,但我注意到你链接的文档的第一段说,如果你创建了一个新的存储库克隆,当你将一个文件夹拆分成一个单独的存储库时,你不会丢失任何Git历史记录或更改。然而,根据对这里所有答案的评论,无论在哪个子目录被重命名的地方,过滤分支和子树脚本都会导致历史的丢失。有什么办法可以解决这个问题吗?找到了保存所有提交的解决方案,包括之前的目录重命名/移动-这是rogerdpack对这个问题的回答。唯一的问题是我不能再使用克隆的repo了。在git的草堆中找到了针!现在我可以保留我所有的提交历史。近乎完美。。。但是您忘记了git过滤器branch-prune empty来删除所有现在为空的旧提交。在推到原点之前要做的事情!如果您犯了错误,并且在删除旧的空提交后仍然希望重新发布,那么执行:git push-u origin master-force with leaseUpvoted以推荐我上个月在使用git filter repo中介绍的新的filter repo工具,这一点肯定是首选方法。它比git filter分支快得多、安全得多,并且可以防止在重写git历史时遇到许多陷阱。希望这个答案能得到更多的关注,因为它是解决git-filter-repo问题的答案。实际上,我目前正在尝试使用git-filter-repo解决问题,但不幸的是,在运行它之后,我丢失了一些文件,这些文件是在提交中添加的,其中包含一个被filter-repo删除的路径。例如:Foo/Foo.cs Bar/Bar.cs都是在同一个提交中添加的。我想在单独的回购协议中移动Foo和Bar。因此,我将我的repo克隆到了一个与新repo名称匹配的文件夹中,并删除了git filter repo-path Foo-Foo-get。我说的是一个更大的回购协议,它适用于所有其他文件,但如果它是这样一个星座的话就不行了。
git filter-repo --path XY1 --path XY2 --path inconsistent --path-rename inconsistent:XY3