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

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

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

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

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想改为:

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

当前回答

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

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) 使用BFG从历史记录中删除不必要的文件夹

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

对于多个文件夹,可以使用逗号java-jar bfg.jar--删除文件夹“{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

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

只需在步骤3中删除XY1、XY2并重命名XYZ->ABC

其他回答

把这个放到你的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'

这不再那么复杂,您只需在repo的克隆上使用gitfilter branch命令,即可选择不需要的子目录,然后推送到新的远程。

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

发现这篇精彩的文章原创参考很容易理解。记录在这里,以防无法访问。

1.准备当前存储库

$ cd path/to/repository
$ git subtree split -P my-folder -b my-folder
Created branch 'my-folder'
aecbdc3c8fe2932529658f5ed40d95c135352eff

文件夹的名称必须是相对路径,从存储库的根目录开始。

2.创建新存储库

$ cd my-folder
$ git init
Initialized empty Git repository in /Users/adamwest/Projects/learngit/shop/my-folder/.git/
$ git add .
$ git commit -m "initial commit"
[master (root-commit) 192c10b] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

这里我们只需要cd到新文件夹,初始化新存储库,并提交任何内容。

3.添加新的远程存储库并推送

$ git remote add origin git@github.com:robertlyall/my-folder.git
$ git push origin -u master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 199 bytes | 199.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:robertlyall/my-folder.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

我们在这里添加远离GitHub的新存储库,然后推送我们的第一次提交。

4.从主存储库中删除文件夹并推送

$ cd ../
$ git rm -rf my-folder
rm 'my-folder/file'
$ git commit -m "Remove old folder"
[master 56aedbe] remove old folder
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 my-folder/file
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 217 bytes | 217.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:robertlyall/shop.git
   74dd8b3..56aedbe  master -> master

最后,我们cd回到rooot目录,从主存储库中删除文件夹,然后提交并推送更改。现在,我们的主存储库中有一个文件夹,但它链接到一个完全独立的存储库,可以在多个项目中重用。

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

这对我有用。我在上面给出的步骤中遇到的问题是

在此命令中,gitfilter branch--prune empty--子目录筛选器FOLDER-NAME branch-NAMEBRANCH-NAME是主如果由于保护问题提交时最后一步失败,请遵循以下步骤:https://docs.gitlab.com/ee/user/project/protected_branches.html

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

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