从一个文件夹创建一个子模块存储库,并保持其 git 提交历史

我有一个网络应用程序,探索其他网络应用程序在一个特定的方式。它包含一些网络演示在 demos文件夹,其中一个演示现在应该有自己的存储库。我想为这个演示应用程序创建一个单独的存储库,并在不丢失其提交历史的情况下从主存储库中将其设置为 子包 子舱

是否有可能从存储库文件夹中的文件保留提交历史,并从中创建一个存储库,而将其用作 子舱

52292 次浏览

详细解决方案

有关使用 npm 的 git 子模块的快速替代方案,请参阅本答案(最后一段)末尾的说明;)

在下面的答案中,您将知道如何从存储库中提取一个文件夹,并从中创建一个 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

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

第一步

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

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

第二步

当前文件夹将是新的存储库,因此请删除当前远程。

git remote rm origin

第三步

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

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

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

第四步

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

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>的跟踪(文件和提交历史记录) ,因此此文件夹的历史记录只存在一次。

这是基于来自 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将在指定的目录中递归地执行此操作:)。现在用 --force推到 origin/master

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 和版本,请参见 Json git urls 作为依赖项

如果这样做,那么作为需求使用的存储库必须是一个 Npm 模块,因此它必须包含一个 package.json文件,否则就会得到这个错误: Error: ENOENT, open 'tmp.tgz-unpack/package.json'

替代解决方案

你可能会发现使用 Npm使用 git url 管理依赖关系更容易:

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

通过@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'

克隆远程,拉所有的分支,改变远程,过滤你的目录,推

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

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

意识到这一点,我发现下面的解决方案对我很有效:

cp .gitattributes .git/info/attributes

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

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

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