从文件夹创建子模块存储库并保留其git commit历史记录


111

我有一个以特定方式浏览其他Web应用程序的Web应用程序。它在一个demos文件夹中包含一些Web演示,并且其中一个演示现在应该具有自己的存储库。我想为此演示应用程序创建一个单独的存储库,并使其成为分包 主存储库中的子模块,而不会丢失其提交历史记录。

是否可以保留存储库文件夹中文件的提交历史记录,并从中创建存储库并将其用作子模块


我一直在搜索如何将目录1从Git存储库A移到Git存储库B。+1以获取文章链接。
Chetabahana 2015年


是的,这的确非常相似,解决方案略有不同,谢谢分享
GabLeRoux,2015年

Answers:


190

详细解决方案

有关使用npm的git子模块的快速替代方法,请参见此答案末尾的注释(最后一段);)

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

受Gerg Bayer的文章启发,将文件从一个Git存储库移动到另一个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

从另一个存储库中的文件夹创建一个新的git存储库

第1步

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

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

第2步

当前文件夹将是新的存储库,因此请删除当前的遥控器。

git remote rm origin

第三步

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

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

现在,您应该拥有一个git存储库,其中包含来自仓库directory 1的根目录中的文件以及所有相关的提交历史记录。

第4步

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

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

您可能需要upstream为第一次推送设置分支

git push --set-upstream origin master

清洁<git repository A>(可选,请参阅评论)

我们要删除的痕迹(文件和提交历史)的<git repository B><git repository A>这样的历史在此文件夹只是有一次。

这基于从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

替换<directory 1>为要删除的文件夹。-r将在指定目录内递归执行:)。现在origin/master--force

git push origin master --force

老板舞台(请参阅下面的注释)

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

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

验证一切是否按预期进行,并 push

git push origin master

注意

完成所有这些操作后,我意识到我的情况更适合使用npm来管理我自己的依赖项。我们可以指定git url和版本,请参阅package.json git urls作为dependencies

如果以这种方式执行此操作,则要用作需求的存储库必须是npm模块,因此它必须包含一个package.json文件,否则会出现此错误:Error: ENOENT, open 'tmp.tgz-unpack/package.json'

tldr(替代解决方案)

您可能会发现使用npm使用git urls管理依赖项更加容易:

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

39
应该避免执行步骤“清理<git存储库A>”。这样做无法完全从历史记录中还原/签出较旧的版本/提交。您应该只git rm文件夹并添加子模块。因此,在签出较早的提交时,请确保具有完整的工作副本。
Cybot 2014年

您不应该cd someLib在第二步之前做吗?您说“当前文件夹将是新的存储库”,但实际上不是。新的存储库(子模块)位于该文件夹内。
Jago 2015年

1
确认:是的,它适用于多个子模块。非常感谢您提供详细的答案。另外,不必使用npm。
Breno Inojosa

2
我想补充的信息有关refs/original/...,在第3步中被创建
埃米尔·伯杰龙

6
GitHub发表了一篇关于如何将文件夹提取到新存储库中的文章: help.github.com/articles/…–
jrobichaud

9

@GabLeRoux的解决方案压缩分支和相关的提交。

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

1-确保您具有此git别名

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

3

GabLeRoux的解决方案效果很好,除非您使用git lfs并且在要分离的目录下有大文件。在这种情况下,在第3步之后,所有大文件都将保留为指针文件,而不是实际文件。我猜可能是由于.gitattributes文件在过滤器分支过程中被删除了。

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

cp .gitattributes .git/info/attributes

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

过滤分支完成后,.gitattributes如果您仍然想对新存储库使用git lfs,请不要忘记放回:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.