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

Gab*_*oux 98 git git-submodules revision-history

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

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

Gab*_*oux 173

详细解决方案

请参阅本答案末尾的注释(最后一段),以便使用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
Run Code Online (Sandbox Code Playgroud)

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

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

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

<git repository B>
    someFolders
    someFiles
Run Code Online (Sandbox Code Playgroud)

从其他存储库中的文件夹创建新的git存储库

步骤1

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

git clone <git repository A url>
cd <git repository A directory>
Run Code Online (Sandbox Code Playgroud)

第2步

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

git remote rm origin
Run Code Online (Sandbox Code Playgroud)

第3步

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

git filter-branch --subdirectory-filter <directory 1> -- --all
Run Code Online (Sandbox Code Playgroud)

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

第4步

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

git remote add origin <git repository B url>
git push
Run Code Online (Sandbox Code Playgroud)

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

git push --set-upstream origin master
Run Code Online (Sandbox Code Playgroud)

清洁<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
Run Code Online (Sandbox Code Playgroud)

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

git push origin master --force
Run Code Online (Sandbox Code Playgroud)

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

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

git submodule add <git repository B url>
git submodule update
git commit
Run Code Online (Sandbox Code Playgroud)

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

git push origin master
Run Code Online (Sandbox Code Playgroud)

注意

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

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

tldr(替代解决方案)

您可能会发现它更容易使用NPM管理使用git的URL依赖:

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

  • 应避免步骤"清除<git存储库A>".这样做无法从历史记录中完全恢复/检出旧版本/提交.你应该只是git rm文件夹并添加子模块.因此,在检查旧提交时,请确保拥有完整的工作副本. (35认同)
  • GitHub发表了一篇关于如何将文件夹提取到新存储库的文章:https://help.github.com/articles/splitting-a-subfolder-out-into-a-new-repository/ (4认同)
  • 我会添加关于在步骤 3 中创建的 `refs/original/...` 的 [信息](http://stackoverflow.com/a/7654880/1218980)。 (2认同)

ood*_*vid 7

@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'
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


ls.*_*ls. 6

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

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

cp .gitattributes .git/info/attributes
Run Code Online (Sandbox Code Playgroud)

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

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

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
Run Code Online (Sandbox Code Playgroud)