如何提取git子目录并从中生成子模块?

如何提取git子目录并从中生成子模块?,git,git-submodules,Git,Git Submodules,几个月前我启动了一个项目,并将所有内容存储在主目录中。 在我的主目录“Project”中有几个包含不同内容的子目录: 项目/文件包含用LaTeX编写的文档 Project/sourcecode/RailsApp包含my rails应用程序 “项目”是数字化的,在“paper”和“RailsApp”目录中都有很多提交。现在,由于我想在我的“RailsApp”中使用cruisecontrol.rb,我想知道是否有一种方法可以在不丢失历史记录的情况下从“RailsApp”生成子模块。如果你想将一些文件

几个月前我启动了一个项目,并将所有内容存储在主目录中。 在我的主目录“Project”中有几个包含不同内容的子目录: 项目/文件包含用LaTeX编写的文档 Project/sourcecode/RailsApp包含my rails应用程序


“项目”是数字化的,在“paper”和“RailsApp”目录中都有很多提交。现在,由于我想在我的“RailsApp”中使用cruisecontrol.rb,我想知道是否有一种方法可以在不丢失历史记录的情况下从“RailsApp”生成子模块。

如果你想将一些文件的子集传输到一个新的存储库,但保留历史记录,你基本上会得到一个全新的历史记录。其工作方式基本如下:

  • 创建新的存储库
  • 对于旧存储库的每个修订版,将对模块的更改合并到新存储库中。这将创建现有项目历史记录的“副本”
  • 如果您不介意编写一个小但毛茸茸的脚本,那么实现自动化应该有点简单。直截了当,是的,但也很痛苦。过去人们用Git重写历史,你可以搜索一下

    或者:克隆存储库,删除克隆中的纸张,删除原始应用程序。这将需要一分钟,它保证会工作,并且你可以回到比试图净化你的git历史更重要的事情上。不要担心历史记录的冗余副本占用硬盘空间。

    签出

    手册页的第页显示了如何将子目录提取到它自己的项目中,同时保留它的所有历史记录并丢弃其他文件/目录的历史记录(正是您要查找的)

    要重写存储库,使其看起来好像
    foodir/
    是其项目根目录,并放弃所有其他历史记录:

       git filter-branch --subdirectory-filter foodir -- --all
    
    因此,例如,您可以将库子目录转换为自己的存储库。
    注意将
    --
    过滤器分支选项与修订选项分开的
    --
    ,以及重写所有分支和标记的
    --all


    实现这一点的一种方法是相反的-删除除要保留的文件以外的所有内容

    基本上,复制存储库,然后使用删除除要保留的文件/文件夹以外的所有内容

    例如,我有一个项目,希望从中将文件
    tvnamer.py
    提取到一个新的存储库:

    git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD
    
    它使用
    git filter branch--tree filter
    完成每个提交,运行命令并重新提交结果目录内容。这是极具破坏性的(因此您应该只在存储库的副本上执行此操作!),并且可能需要一段时间(对于包含300个提交和大约20个文件的存储库,大约需要1分钟)

    上面的命令只是在每个修订版上运行以下shell脚本,当然您必须对其进行修改(使其排除子目录而不是
    tvnamer.py
    ):

    最大的明显问题是它会留下所有提交消息,即使它们与剩余文件无关。脚本修复了这个

    git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
    
    您需要将
    -f
    强制参数run
    过滤器分支
    再次与
    refs/original/
    中的任何内容一起使用(这基本上是一个备份)

    当然,这永远不会是完美的,例如,如果您的提交消息提到其他文件,但它与git current所允许的最接近(据我所知)

    同样,只能在存储库的副本上运行此操作-总之,要删除除“ThismyFileName.txt”以外的所有文件:


    现在有一种比手动使用git filter branch更简单的方法:

    装置 注意
    git子树
    现在是从1.7.11开始的
    git
    (如果您安装了contrib)的一部分,因此您可能已经安装了它。您可以通过执行
    git子树
    进行检查


    要从源代码安装git子树(对于较旧版本的git):

    或者如果你想要手册页等等

    make doc
    make install
    
    用法 将较大的数据块拆分为较小的数据块:

    # Go into the project root
    cd ~/my-project
    
    # Create a branch which only contains commits for the children of 'foo'
    git subtree split --prefix=foo --branch=foo-only
    
    # Remove 'foo' from the project
    git rm -rf ./foo
    
    # Create a git repo for 'foo' (assuming we already created it on github)
    mkdir foo
    pushd foo
    git init
    git remote add origin git@github.com:my-user/new-project.git
    git pull ../ foo-only
    git push origin -u master
    popd
    
    # Add 'foo' as a git submodule to `my-project`
    git submodule add git@github.com:my-user/new-project.git foo
    
    有关详细文档(手册页),请阅读
    git subtree.txt

    ,两者的答案非常相似。我在两人之间来回奔波,试图理解其中任何一人身上缺失的部分。下面是它们的组合

    首先将Git Bash导航到要拆分的Git回购的根目录。在我的示例中,这是
    ~/Documents/OriginalRepo(master)

    下面是上面的一个副本,替换了可自定义的名称,并改用https。根文件夹现在是
    ~/Documents/\u Shawn/UnityProjects/SoProject(master)


    这也是一个很好的答案:git子树可能与之重复,但这不是避免使用子模块的关键吗?我的意思是,您确实是git子树的作者(除非有昵称冲突),但是看起来git子树已经改变了,尽管您显示的命令似乎仍然有效。我说得对吗?从1.7.11开始,git子树现在是git的一部分(如果您安装contrib的话)。Well
    git rm-rf./foo
    HEAD
    中删除
    foo
    ,但不过滤
    我的项目的全部历史记录。然后,
    git子模块添加git@github.com:my user/new-project.git foo
    仅使
    foo
    成为从
    头部开始的子模块。在这方面,脚本
    过滤器分支
    更为优越,因为它允许实现“从一开始就好像subdir是一个子模块一样”的thx——git子树文档有点令人困惑,这是(对我来说)我想用它做的最明显的有用的事情……看起来这只是Mac:(
    git filter分支
    有(现在?)一个删除空提交的内置选项,即
    --prune empty
    git过滤器分支
    的一个更好的指南是在这个问题的答案中
    git clone https://github.com/apenwarr/git-subtree.git
    
    cd git-subtree
    sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree
    
    make doc
    make install
    
    # Go into the project root
    cd ~/my-project
    
    # Create a branch which only contains commits for the children of 'foo'
    git subtree split --prefix=foo --branch=foo-only
    
    # Remove 'foo' from the project
    git rm -rf ./foo
    
    # Create a git repo for 'foo' (assuming we already created it on github)
    mkdir foo
    pushd foo
    git init
    git remote add origin git@github.com:my-user/new-project.git
    git pull ../ foo-only
    git push origin -u master
    popd
    
    # Add 'foo' as a git submodule to `my-project`
    git submodule add git@github.com:my-user/new-project.git foo
    
    # move the folder at prefix to a new branch
    git subtree split --prefix=SubFolderName/FolderToBeNewRepo --branch=to-be-new-repo
    
    # create a new repository out of the newly made branch
    mkdir ~/Documents/NewRepo
    pushd ~/Documents/NewRepo
    git init
    git pull ~/Documents/OriginalRepo to-be-new-repo
    
    # upload the new repository to a place that should be referenced for submodules
    git remote add origin git@github.com:myUsername/newRepo.git
    git push -u origin master
    popd
    
    # replace the folder with a submodule
    git rm -rf ./SubFolderName/FolderToBeNewRepo
    git submodule add git@github.com:myUsername/newRepo.git SubFolderName/FolderToBeNewRepo
    git branch --delete --force to-be-new-repo
    
    # move the folder at prefix to a new branch
    git subtree split --prefix=Assets/SoArchitecture --branch=so-package
    
    # create a new repository out of the newly made branch
    mkdir ~/Documents/_Shawn/UnityProjects/SoArchitecture
    pushd ~/Documents/_Shawn/UnityProjects/SoArchitecture
    git init
    git pull ~/Documents/_Shawn/UnityProjects/SoProject so-package
    
    # upload the new repository to a place that should be referenced for submodules
    git remote add origin https://github.com/Feddas/SoArchitecture.git
    git push -u origin master
    popd
    
    # replace the folder with a submodule
    git rm -rf ./Assets/SoArchitecture
    git submodule add https://github.com/Feddas/SoArchitecture.git
    git branch --delete --force so-package