如何从Git仓库中将模块分离成独立仓库并保留其提交历史?
起因
- 部门代码管理使用的SVN,由于Git可以提交至本地,自己在本地又使用Git管理代码,使用Git管理了一整个工程,在开发不同模块时切到不同分支,一开始没把模块独立放到一个Git仓库,现在想独立模块,又想保留其提交历史,故有此文。
参考
- subtrees in Git: How to split Directories into individual standalone repositories
- assertion failed errors when trying to git subtree split
- Git clone –recurse-submodules throws error on MacOs: Transmission type ‘file’ not allowed
- Git 工具 - 子模块
操作
- 将子目录拆分独立库:
git subtree split -P <name-of-folder> -b <name-of-new-branch>
,注意此行命令需要在Git仓库toplevel目录执行,<name-of-folder>
需要避免在前面加./
,避免使用反斜杠,否则会产生assertion failed errors,解决方案就是改掉就行,参考:assertion failed errors when trying to git subtree split - 拆分独立库后此仓库在
<name-of-new-branch>
分支中会保存模块代码和提交记录,然后mkdir <name-of-new-branch> && cd <name-of-new-branch> && git
,我参考别人的操作使用git pull </path/to/big-repo> <name-of-new-branch>
拉取上级目录的指定分支到一个新的文件夹好像不太行,网上暂时没查到/path/to
的用法。我使用的方式是将老仓库<name-of-new-branch>
分支代码push,然后使用git pull <repo-path.git> <name-of-new-branch>
拉来的代码。 - 上一步操作后就已经将代码和历史提交记录全部拉到了一个新仓库,后续就可以将新仓库Push。
如何在工程的Git仓库中引用模块仓库?
参考Git文档:Git 工具 - 子模块
我们首先将一个已存在的 Git 仓库添加为正在工作的仓库的子模块。 你可以通过在 git submodule add 命令后面加上想要跟踪的项目的相对或绝对 URL 来添加新的子模块。 在本例中,我们将会添加一个名为 “DbConnector” 的库。
$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into ‘DbConnector’…
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity… done.
默认情况下,子模块会将子项目放到一个与仓库同名的目录中,本例中是 “DbConnector”。 如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径。如果一个分支有子模块,另一个分支没有,直接 checkout 后会造成子模块的文件在另一个分支未被删除,可以在 checkout 前使用
git submodule deinit --all
命令来卸载当前分支已安装的所有子模块,在 checkout 后,如果另一个分支也有子模块,可以使用git submodule init
命令注册子模块,再使用git submodule update
从子模块库中取出文件。我使用的子模块库是本地的Git仓库,在拉库的时候首次会报
Transmission type 'file' not allowed
的错误,需通过git config --global protocol.file.allow always
配置Git,参考:Git clone –recurse-submodules throws error on MacOs: Transmission type ‘file’ not allowed
Git子模块和子树区别
Git子模块(submodule)和子树(subtree)都是Git中用于将外部仓库包含到自己的仓库中的机制。虽然它们的目的相似,但在功能和使用方式上有一些区别。
Git子模块:
- 子模块是对外部仓库中特定提交的引用。
- 当你将一个子模块添加到你的仓库时,你在自己的仓库中包含了指向另一个仓库的链接,它作为一个子目录存在。
- 子模块维护着独立的Git历史,被视为独立的仓库。它们有自己的分支、标签和提交历史。
- 每个子模块引用指向外部仓库中的特定提交。你可以通过显式地拉取变更来更新子模块到新的提交。
- 子模块通常用于在你的仓库中包含另一个项目作为依赖项,但希望保持两个代码库的分离。
Git子树:
- 子树允许你直接将外部仓库的内容嵌入到自己仓库的子目录中。
- 当你向你的仓库添加子树时,你将另一个仓库的文件导入并合并到你的仓库的子目录中。导入的文件成为你的仓库历史的一部分。
- 子树不维护独立的Git历史。相反,外部仓库的提交会合并到你的仓库的历史中。
- 子树允许你在你的仓库中直接对导入的代码进行修改。如果你有写入权限,你也可以将修改的内容推送回原始仓库。
- 子树通常用于将另一个项目的代码作为你仓库的一部分,并将其视为你代码库的一个组成部分。
总结而言,子模块提供了一种将外部仓库作为独立实体包含在你的仓库中的方式,而子树允许你将外部仓库的内容合并到你的仓库历史中。选择使用子模块还是子树取决于你的具体需求和工作流程。