单个文件作为 Git 子模块

Posted

技术标签:

【中文标题】单个文件作为 Git 子模块【英文标题】:Single file as Git submodule 【发布时间】:2011-05-09 10:15:51 【问题描述】:

我正在尝试确定 Git 存储库之间共享代码的最佳实践。

到目前为止,我显然遇到了一些子模块,它们似乎——几乎——符合要求。我的项目是一个结构简单的php MVC框架:

/app core.php /核心

其中app 是一个文件夹,其中包含特定于应用程序的控制器、模型、视图等,而core 包含一般用途的文件夹,例如登录控制器。 core.php 文件本身是所有请求的全局处理程序。

因此,我在此 MVC 框架的所有部署中的共享代码是 core.phpcore

我可以看到如何将 core 转换为 Git 子模块,但不能将 core.php

这甚至可能吗?我是否需要重新构建我的框架,以便 core.php 驻留在 core 文件夹中,这样我就可以将整个文件夹设为子模块,还是有更好的方法?

【问题讨论】:

【参考方案1】:

如果您可以使用符号链接(例如,您没有使用 Windows),那么您可以像这样设置 corecore.php

# "base" repository layout:
core/
core.app

# each app repository layout:
base/
  core/
  core.php
core -> base/core/
core.php -> base/core.php
app/

在每个应用程序存储库中,base/ 目录要么是使用“基础”存储库的子模块,要么是“基础”存储库的子树合并。

这两种方法都可以让您开始在特定应用的上下文中对基本代码进行更改,然后将这些更改拉回主基本存储库。使用子模块时,您必须注意始终在发布任何引用这些新基本提交的应用程序提交之前发布新的基本提交(使用子树合并时这不是问题,因为每个应用程序都是“扁平的”并且有效地拥有自己的副本基础)。

如果您决定不使用子模块,第三方git subtree 命令似乎是管理子树合并的一种非常好的方法。

子树

git init newapp
cd newapp
ln -s base/core
ln -s base/core.php
git add core core.php
git commit -m'point to base (to be added next)'

# hook up base
git subtree add --prefix=base git@git.example.com:me/app_base.git master

mkdir app
# edit app/bar.php

# update base
git subtree pull --prefix=base git@git.example.com:me/app_base.git master

.
|-- .git/
|   |-- ...
|   `-- ...
|-- app/
|   `-- bar.php
|-- base/
|   |-- core/
|   |   `-- foo.php
|   `-- core.php
|-- core -> base/core/
`-- core.php -> base/core.php

子模块

git init newapp
cd newapp
ln -s base/core
ln -s base/core.php
git add core core.php
git commit -m'point to base (to be added next)'

# hook up "base"
git submodule add git@git.example.com:me/app_base.git base
git commit -m'incorporate base'

mkdir app
# edit app/bar.php

# update base
(cd base && git fetch origin && git merge origin/master)
git add base
git commit -m'updated base'
.
|-- .git/
|   |-- ...
|   `-- ...
|-- .gitmodules
|-- app/
|   `-- bar.php
|-- base/
|   |-- .git/
|   |   |-- ...
|   |   `-- ...
|   |-- core/
|   |   `-- foo.php
|   `-- core.php
|-- core -> base/core/
`-- core.php -> base/core.php

【讨论】:

Windows Vista 及更高版本支持 NTFS 上的符号链接。 ...还有 Vista 之前的版本。从来不明白为什么这么多人说 Windows(或 NTFS)不能做到这一点。【参考方案2】:

也许您最好将 core.php 和 core 维护在一个单独的 repo 中,然后将其用作远程。 然后,您可以通过将其拉入它使用的任何项目来管理它。为此,只需将新项目作为单独的 git 存储库启动,然后将“核心”存储库作为子树拉入。

本章向您展示如何做到这一点:

更新参考:http://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_subtree_merge 原文参考:https://git-scm.com/book/en/v1/Git-Tools-Subtree-Merging

这比本书上一节 (6.6) 中建议的设置要好一些。

看看;这可能会有所帮助。

【讨论】:

谢谢 rmk,看起来read-tree 绝对是一个可行的解决方案。只是为了澄清(我认为这是您链接的最后几段所表明的),因此是否可以在我的应用程序存储库中对core* 进行更改并将其合并回框架的存储库而不带来所有应用程序特定的代码没有cherry-pick? 是的,您可以通过 git-push 将更改从应用程序仓库推送到核心。 注意:progit 书的链接给了404。原文可以在git-scm.com/book/en/v1/Git-Tools-Subtree-Merging(书的第1版),书的第2版也有这个相同的主题,作为一个更广泛页面的子部分:git-scm.com/book/en/v2/…【参考方案3】:

子模块是一个 git 仓库,有自己的 .git 目录,所以它必须包含在一个目录中。我不相信有任何方法可以轻松解决这个问题。您将不得不以某种方式将您的东西打包到一个目录中 - 如果 core.php 与核心中的东西一起使用,那么将它们放在一个子模块存储库中是完全有意义的!

rmk's answer,建议你在一个 repo 中完成这一切,使用 core 和 core.php 作为起点是另一个合理的起点。您应该根据预期的工作流程做出决定。如果您计划将 core* 内容与使用它的项目分开修改,则子模块会很好;然后,您可以更新使用它的各种项目中的子模块。如果您想修改核心* 内容以适应特定项目,基线存储库会很好;然后,您可以从基线 repo 中提取更新,将它们与您在项目 repo 中所做的更改合并。

【讨论】:

感谢 Jefromi 的明确解释,我也这么想。我的工作流程是,我希望协作者能够在特定于应用程序的存储库或框架的存储库中编辑 core* 文件,并让这些更改可以在任一方向合并,而无需将特定于应用程序的代码带入框架存储库,也不必总是不得不求助于git cherry-pick。对于子模块或 rmk 的解决方案,这是否有意义 - 并且听起来最可行? @Will:这里有两种主要方法。可能更简单的一个是git-subtree,我个人从未使用过它,但它完全是关于将子树从项目中合并和拆分回项目。另一种方法是对 core* 文件的开发非常小心 - 在只有它们的主题分支上进行所有操作,以便它们可以干净地合并到核心项目和任何其他项目中。 感谢 Jefromi,完美。看起来git-subtree 是我基于 Pro Git 书的示例的前进方向。

以上是关于单个文件作为 Git 子模块的主要内容,如果未能解决你的问题,请参考以下文章

我可以开发一个节点模块作为 GIT 子模块吗

RestKit 作为 Git 子模块安装后出现错误

在托管 git 子模块的 docker 中使用 Yarn

如何将 git 子模块集成到 git 工作流

添加 git 子模块作为对 VS 中现有解决方案的引用

子模块内的 Git 子模块(嵌套子模块)