从一个文件夹创build一个子模块仓库,并保持其git提交历史logging

我有一个Web应用程序,以特定的方式探索其他Web应用程序。 它包含demos文件夹中的一些网页演示,其中一个demos现在应该有它自己的存储库。 我想为这个演示应用程序创build一个单独的存储库,并使其成为一个 分装 来自主存储库的子模块 ,而不会丢失其提交历史logging。

是否有可能保留库文件夹中的文件提交历史logging,并从它创build一个存储库,并将其作为一个子模块使用?

    详细解决scheme

    请参阅本答案末尾的注释(最后一段),以便使用npm快速替代git子模块;)

    在下面的答案中,您将知道如何从存储库中提取文件夹,并从中创build一个git存储库,然后将其作为子模块而不是文件夹包含在其中。

    启发从Gerg Bayer的文章从一个Git存储库移动文件到另一个,保留历史

    一开始,我们有这样的东西:

     <git repository A> someFolders someFiles someLib <-- we want this to be a new repo and a git submodule! some files 

    在下面的步骤中,我将引用这个someLib作为<directory 1>

    最后,我们会有这样的事情:

     <git repository A> someFolders someFiles @submodule --> <git repository B> <git repository B> someFolders someFiles 

    从其他存储库中的文件夹创build一个新的git存储库

    步骤1

    获取要分割的存储库的全新副本。

     git clone <git repository A url> cd <git repository A directory> 

    第2步

    当前的文件夹将是新的存储库,所以删除当前的远程。

     git remote rm origin 

    第3步

    提取所需文件夹的历史logging并提交

     git filter-branch --subdirectory-filter <directory 1> -- --all 

    你现在应该有一个git仓库,其directory 1中的文件包含所有相关的提交历史logging。

    步骤4

    创build您的在线存储库并推送您的新存储库!

     git remote add origin <git repository B url> git push 

    您可能需要设置upstream分支进行首次推送

     git push --set-upstream origin master 

    清理<git repository A> (可选,请参阅注释)

    我们想从<git repository A>删除<git repository B>跟踪(文件和提交历史),所以这个文件夹的历史logging只有一次。

    这是基于从github 删除敏感数据 。

    转到一个新的文件夹和

     git clone <git repository A url> cd <git repository A directory> git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all 

    用你想删除的文件夹replace<directory 1>-r将在指定的目录内recursion地执行它:)。 现在用--force推到origin/master

     git push origin master --force 

    老板阶段(见下面的注释)

    <git repository B>创build一个子模块到<git repository A>

     git submodule add <git repository B url> git submodule update git commit 

    validation是否一切按预期工作,并push

     git push origin master 

    注意

    做完所有这些之后,我意识到在我的情况下,使用npm来pipe理自己的依赖关系更合适。 我们可以指定git的URL和版本,请参阅package.json git urls作为依赖项 。

    如果你这样做,你想要作为一个需求的存储库必须是一个npm模块,所以它必须包含一个package.json文件,否则你会得到这个错误: Error: ENOENT, open 'tmp.tgz-unpack/package.json'

    tldr(替代解决scheme)

    您可能会发现使用npm更容易,并使用git urlpipe理依赖关系 :

    • 将文件夹移动到新的存储库
    • 在两个存储库中运行npm init
    • 运行npm install --save git://github.com/user/project.git#commit-ish你想要安装依赖的地方

    GabLeRoux的解决scheme运行良好,除非您使用git lfs并且在您要分离的目录下有大文件。 在这种情况下,在步骤3之后,所有的大文件将保持为指针文件而不是真实文件。 我想这可能是由于在filter分支进程中删除.gitattributes文件。

    意识到这一点,我发现以下解决scheme适用于我:

     cp .gitattributes .git/info/attributes 

    复制git lfs使用的.gitattributes来跟踪大文件到.git/目录以避免被删除。

    当filter分支完成后,如果你仍想使用git lfs作为新的存储库,不要忘记把.gitattributes放回去:

     mv .git/info/attributes .gitattributes git add .gitattributes git commit -m 'added back .gitattributes' 

    @GabLeRoux的解决scheme压缩了分支和相关的提交。

    克隆和保留所有额外分支和提交的简单方法:

    1 – 确保你有这个混帐别名

     git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t' 

    2 – 克隆远程,拉动所有分支,更换遥控器,过滤目录,按下

     git clone git@github.com:user/existing-repo.git new-repo cd new-repo git clone-branches git remote rm origin git remote add origin git@github.com:user/new-repo.git git remote -v git filter-branch --subdirectory-filter my_directory/ -- --all git push --all git push --tags