程序员经常重用代码。事实上,这是任何优秀代码库的核心原则之一——不要重复自己(DRY)。如果您想在多个其他存储库中使用共享项目怎么办?该 git subtree 命令可以帮助管理它。
代码重用问题
将项目嵌入到其他项目中会带来问题;项目 1 和项目 2 都连接到 Git,但直接为它们使用共享子项目并不是一个好的设计选择。这本质上是分叉子项目以在两个地方使用它,并且不可能维护子项目的正式版本。
这个问题有几个解决方案,每个都有自己的缺点。
首先,最明显的解决方案是将子项目制作成一个包,并将其分发到 NPM 或 NuGet 等包管理器上。这对于不经常更新或维护的东西非常有效,并且可以负担得起以谨慎的版本号分发给他们的消费者。但是,如果您定期更改此代码,则必须从第三方源集成、发布和拉取项目的新版本,这与直接访问代码一样不起作用。它还为当地的发展带来了复杂性。
另一种解决方案是使用 monorepo,一个用于所有代码的巨型存储库。这并不像您想象的那么疯狂,并且如果您的所有代码都在同一个域中,则效果很好;Google 为他们的所有代码使用一个 monorepo,而微软为他们维护的所有 .NET 程序集使用一个 monorepo。这样就解决了问题,因为如果你修改了子项目中的代码,每当你重新构建时,它都会被更新。在 Visual Studio 中,这可以通过Project References轻松完成。
但是,在许多情况下,您希望两全其美——将其作为一个包集中维护,但也允许在多个项目中直接嵌入和编辑。为此,Git Subtree 提供了一个解决方案。
核心概念非常简单:您可以拥有更小的 Git 存储库,它们的上游链接到子存储库,但嵌入到另一个项目中。项目 1、项目 2 和子项目的所有更改都在它们自己的存储库中进行跟踪。
通常,Git 足够聪明,可以自动处理推送和合并,具体取决于哪些更改来自哪个子树。但最好不要在子树代码和主项目代码之间混合提交,因为在某些情况下,您可能会遇到更复杂的合并 ,这需要您使用底层的 Git 工具来git subtree 包装。
设置 Git 子树
如果您只是设置一个空项目,并且要设置子树,则需要进行初始提交——即使它是空的——否则 Git 会抛出一个关于 HEAD 不明确的错误。您可以使用以下命令进行空提交:
git commit --allow-empty -n -m "Initial commit."
您需要为子项目添加遥控器,并为其命名。您将使用此名称来引用它:
git remote add -f SubTreeName https://github.com/user/project.git
然后,您可以在给定的前缀处添加子树。使用该–squash 命令,使整个子项目历史不会存储在主项目中。
git subtree add --prefix .Path/To/SubTree SubTreeName master --squash
使用 Git 子树
当您需要拉取时,Git 只会自动更新主项目,因此您必须获取远程,然后使用子树特定的拉取命令。这有点冗长,因为您需要传入前缀,但可以完成工作:
git fetch SubTreeName master
git subtree pull --prefix .Path/To/SubTree SubTreeName SubTreeName master --squash
请注意,您可以从远程获取提交,但将它们拉入子树或将它们推送到远程需要您git subtree 专门使用。
当需要回馈上游时,您需要使用git subtree push:
git subtree push --prefix=.Path/To/SubTree SubTreeName master