请查看本答案末尾的注释(最后一段)以了解使用npm快速替代git子模块的方法 ;)
在下面的回答中,您将了解如何从存储库中提取一个文件夹,并将其作为Git存储库包含为子模块,而不是一个文件夹。
受 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 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 仓库 A>
(可选,详见注释)我们希望从 <git 仓库 A>
中删除 <git 仓库 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 代码库 B>
创建一个子模块到<git 代码库 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 as dependencies。
如果您以这种方式进行操作,则要使用作为要求的存储库必须是一个npm模块,因此它必须包含一个package.json
文件,否则您将收到此错误:Error: ENOENT, open 'tmp.tgz-unpack/package.json'
。
您可能会发现使用npm和通过git urls管理依赖关系更容易:
npm init
npm install --save git://github.com/user/project.git#commit-ish
cd someLib
吗?你说“当前文件夹将成为新的存储库”,但实际上并不是这样;新的存储库(子模块)在那个文件夹内部。 - Jago@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
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'
filter-branch
已被filter-repo
取代。
使用filter-repo
将子文件夹拆分出来的步骤在此处有详细说明:
https://docs.github.com/en/get-started/using-git/splitting-a-subfolder-out-into-a-new-repository
在GabLeRoux的回答的第3步中使用:git filter-repo --path FOLDER-NAME/