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

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

问题描述

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



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



我想我可以做一个克隆并删除每个克隆中不需要的部分,但我想这会在检查一个更老的修改等。这可能是可以接受的,但我宁愿能假装这两个存储库没有共享历史记录。



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

  XYZ / 
.git /
XY1 /
ABC /
XY2 /

但我想这个:

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


解决方案

更新:这个过程很常见,那就是t他的git团队使用新工具 git subtree 更简单。看到这里:将子目录分离(移动)到单独的Git仓库




您想克隆您的存储库,然后使用 git filter-branch 标记所有内容,但在新回购库中需要的子目录被垃圾回收。


$ b


  1. 克隆本地存储库:

      git clone / XYZ / ABC 

    (注意:存储库将使用硬链接进行克隆,但这不是问题,因为硬链接文件本身不会被修改 - 将会创建新文件。)<现在,让我们保留我们想要重写的有趣分支,然后删除原点以避免推到那里并确保旧的提交将会不被原产地引用:

      cd / ABC 
    为分支1中的i br2 br3;做混帐分支-t $ i原产地/ $我;完成
    git remote rm来源

    或者所有远程分行:

      cd / ABC 
    for $ in $(git branch -r | seds /.* origin\ ///);做混帐分支-t $ i原产地/ $我;完成
    git remote rm origin


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

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

      git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC  -   - 所有

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

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


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

      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

  5. 应该确实有附加参数 - --all 。是的,这真的是破折号空间短划线破折号所有。这需要是该命令的最后一个参数。正如Matli发现的那样,这样可以将项目分支和标签包含在新的回购库中。

    编辑:将以下评论的各种建议合并在一起,以确保资源库实际上是收缩的(这并非总是如此)。


    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/
    

    But I would like this instead:

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

    解决方案

    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


    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. 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.)

    2. 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
      

      or for all remote branches:

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

    3. 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.

    4. 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
      

      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
      

    5. 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
      

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

    Note: For most uses, git filter-branch should indeed have the added parameter -- --all. Yes that's really dash dash space dash dash 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.

    Edit: 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天全站免登陆