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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17413493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 16:28:58  来源:igfitidea点击:

Create a submodule repository from a folder and keep its git commit history

gitgit-submodulesrevision-history

提问by GabLeRoux

I have a web application that explores other web applications in a particular way. It contains some web demos in a demosfolder and one of the demo should now have it's own repository. I would like to create a separate repository for this demo application and make it a subpackagesubmodulefrom main repository without losing its commit history.

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

Is it possible to keep the commit history from the files in a repository's folder and create a repository from it and use it as a submoduleinstead?

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

回答by GabLeRoux

Detailed Solution

详细解决方案

See the note at the end of this answer (last paragraph) for a quick alternative to git submodules using npm ;)

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

In the following answer, you will know how to extract a folder from a repository and make a git repository from it and then including it as a submoduleinstead of a folder.

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

Inspired from Gerg Bayer's article Moving Files from one Git Repository to Another, Preserving History

灵感来自 Gerg Bayer 的文章将文件从一个 Git 存储库移动到另一个,保存历史

At the beginning, we have something like this:

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

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

In the steps bellow, I will refer this someLibas <directory 1>.

在下面的步骤中,我将其someLib称为<directory 1>.

At the end, we will have something like this:

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

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

<git repository B>
    someFolders
    someFiles

Create a new git repository from a folder in an other repository

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

Step 1

第1步

Get a fresh copy of the repository to split.

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

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

Step 2

第2步

The current folder will be the new repository so remove the current remote.

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

git remote rm origin

Step 3

第 3 步

Extract history of the desired folder and commit it

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

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

You should now have a git repository with the files from directory 1in your repo's root with all related commit history.

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

Step 4

第四步

Create your online repository and push your new repository!

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

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

You may need to set the upstreambranch for your first push

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

git push --set-upstream origin master

Clean <git repository A>(optional, see comments)

清洁<git repository A>(可选,见评论)

We want to delete traces (files and commit history) of <git repository B>from <git repository A>so history for this folder is only there once.

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

This is based on Removing sensitive datafrom github.

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

Go to a new folder and

转到一个新文件夹并

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>by the folder you want to remove. -rwill do it recursively inside the specified directory :). Now push to origin/masterwith --force

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

git push origin master --force

Boss Stage (See Note below)

Boss 阶段(见下面的注释)

Create a submodulefrom <git repository B>into <git repository A>

从into创建子模块<git repository B><git repository A>

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

Verify if everything worked as expected and push

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

git push origin master

Note

笔记

After doing all of this, I realized in my case that it was more appropriate to use npmto manage my own dependencies instead. We can specify git urls and versions, see the package.json git urls as dependencies.

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

If you do it this way, the repository you want to use as a requirement must be an npm moduleso it must contain a package.jsonfile or you'll get this error: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

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

tldr (alternative solution)

tldr(替代解决方案)

You may find it easier to use npmand manage dependencies with git urls:

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

  • Move folder to a new repository
  • run npm initinside both repositories
  • run npm install --save git://github.com/user/project.git#commit-ishwhere you want your dependencies installed
  • 将文件夹移动到新的存储库
  • npm init在两个存储库中 运行
  • 运行npm install --save git://github.com/user/project.git#commit-ish您希望安装的依赖关系

回答by oodavid

The solution by @GabLeRoux squashes the branches, and the related commits.

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

A simple way to clone and keep all those extra branches and commits:

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

1 - Make sure you have this git alias

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 - Clone the remote, pull all branches, change the remote, filter your directory, push

2 - 克隆远程,拉所有分支,更改远程,过滤您的目录,推送

git clone [email protected]:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin [email protected]:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

回答by ls.

GabLeRoux's solution works well except if you use git lfsand has large files under the directory you want to detach. In that case, after step 3 all the large files will remain to be pointer files instead of real files. I guess it's probably due to the .gitattributesfile being removed in the filter branch process.

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

Realizing this, I find the following solution works for me:

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

cp .gitattributes .git/info/attributes

Copying .gitattributeswhich git lfs uses to track large files to .git/directory to avoid being deleted.

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

When filter-branch is done don't forget to put back the .gitattributesif you still want to use git lfs for the new repository:

当 filter-branch 完成后,.gitattributes如果你仍然想对新存储库使用 git lfs ,请不要忘记放回:

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