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

时间:2013-07-01 20:52:44

标签: git git-submodules revision-history

我有一个以特定方式探索其他Web应用程序的Web应用程序。它包含demos文件夹中的一些Web演示,其中一个演示现在应该拥有自己的存储库。我想为这个演示应用程序创建一个单独的存储库,并从主存储库中将其作为 subpackage submodule,而不会丢失其提交历史记录。

是否可以保留存储库文件夹中文件的提交历史记录并从中创建存储库并将其用作submodule而不是?

3 个答案:

答案 0 :(得分:162)

详细解决方案

  

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

在下面的答案中,您将了解如何从存储库中提取文件夹并从中创建一个git存储库,然后将其包含为submodule而不是文件夹。

灵感来自Gerg Bayer的文章Moving Files from one Git Repository to Another, Preserving History

一开始,我们有这样的事情:

<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

第3步

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

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

您现在应该拥有一个git存储库,其中包含来自repo根目录中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的Removing sensitive data

转到新文件夹

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

Boss Stage(见下面的注释)

<git repository B>创建submodule<git repository A>

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

验证一切是否按预期工作并push

git push origin master

注意

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

如果您这样做,您要用作要求的存储库必须是 npm模块,因此它必须包含package.json文件,否则您将收到此错误: Error: ENOENT, open 'tmp.tgz-unpack/package.json'

tldr(替代解决方案)

您可能会发现使用npmmanage dependencies with git urls更容易:

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

答案 1 :(得分:6)

@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

答案 2 :(得分:2)

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

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

cp .gitattributes .git/info/attributes

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

当filter-branch完成时,如果您仍想将git lfs用于新存储库,请不要忘记放回.gitattributes

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