将子目录分离(移动)到单独的 Git 存储库中 [英] Detach (move) subdirectory into separate Git repository

查看:18
本文介绍了将子目录分离(移动)到单独的 Git 存储库中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Git repository which contains a number of subdirectories. Now I have found that one of the subdirectories is unrelated to the other and should be detached to a separate repository.

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

How can I do this while keeping the history of the files within the subdirectory?

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

I guess I could make a clone and remove the unwanted parts of each clone, but I suppose this would give me the complete tree when checking out an older revision etc. This might be acceptable, but I would prefer to be able to pretend that the two repositories doesn't have a shared history.

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

Just to make it clear, I have the following structure:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我更喜欢这个:

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

推荐答案

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

Update: This process is so common, that the git team made it much simpler with a new tool, git subtree. See here: Detach (move) subdirectory into separate Git repository

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

You want to clone your repository and then use git filter-branch to mark everything but the subdirectory you want in your new repo to be garbage-collected.

  1. 要克隆您的本地存储库:

  1. To clone your local repository:

git clone /XYZ /ABC

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

(Note: the repository will be cloned using hard-links, but that is not a problem since the hard-linked files will not be modified in themselves - new ones will be created.)

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

Now, let us preserve the interesting branches which we want to rewrite as well, and then remove the origin to avoid pushing there and to make sure that old commits will not be referenced by the origin:

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 changed 所有标签(因为它们都与子项目无关);此外,删除此类标签后,将回收更多空间.显然 git filter-branch 应该能够重写其他标签,但我无法验证这一点.如果要删除所有标签,请使用 git tag -l |xargs git tag -d.

  • Now you might want to also remove tags which have no relation with the subproject; you can also do that later, but you might need to prune your repo again. I did not do so and got a WARNING: Ref 'refs/tags/v0.1' is unchanged for all tags (since they were all unrelated to the subproject); additionally, after removing such tags more space will be reclaimed. Apparently git filter-branch should be able to rewrite other tags, but I could not verify this. If you want to remove all tags, use git tag -l | xargs git tag -d.

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

    Then use filter-branch and reset to exclude the other files, so they can be pruned. Let's also add --tag-name-filter cat --prune-empty to remove empty commits and to rewrite tags (note that this will have to strip their signature):

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

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

    or alternatively, to only rewrite the HEAD branch and ignore tags and other branches:

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

  • 然后删除备份的reflogs,以便真正回收空间(虽然现在操作是破坏性的)

  • Then delete the backup reflogs so the space can be truly reclaimed (although now the operation is destructive)

    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 存储库,并保留了其所有历史记录.

    and now you have a local git repository of the ABC sub-directory with all its history preserved.

    注意:对于大多数用途,git filter-branch 确实应该添加参数-- --all.是的,这真的 --space-- all.这需要是命令的最后一个参数.正如 Matli 发现的那样,这将保留项目分支和标签包含在新的存储库中.

    Note: For most uses, git filter-branch should indeed have the added parameter -- --all. Yes that's really --space-- all. This needs to be the last parameters for the command. As Matli discovered, this keeps the project branches and tags included in the new repo.

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

    various suggestions from comments below were incorporated to make sure, for instance, that the repository is actually shrunk (which was not always the case before).

    这篇关于将子目录分离(移动)到单独的 Git 存储库中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆