是否有在本地修改 Git 子模块的正确方法?

Is there a correct way to modify a Git submodule locally?

在下面的示例中,我想证明对 Git 子模块进行更改并在本地提交可能会导致非常糟糕的情况,在这种情况下我想找到一个可行的解决方案(除了答案 "Dude, don't use Git sub-modules it's evil")

例子

我首先创建两个存储库:一个主项目及其子模块:

~ $ for r in main sub; do mkdir $r; cd $r; git init; cd ..; done
Initialized empty Git repository in ~/main/.git/
Initialized empty Git repository in ~/sub/.git/

让我们把一些内容放到子模块中:

~/ $ cd sub
~/sub $ touch sub && git add sub
~/sub $ git commit -m "Added sub"
[master (root-commit) a14ce2f] Added sub
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 sub

然后我们在主项目中导入子模块:

~/sub $ cd ../main
~/main $ git submodule add ../sub sub
Cloning into '~/main/sub'...
done.
~/main $ git commit -am "Added submodule sub"
[master (root-commit) bd37219] Added submodule sub
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 sub

现在的问题是:我想在本地修改子模块的猴子补丁。

~/main $ cd sub
~/main/sub $ echo "Monkey patched"" >> sub
> echo "Monkey patched"^C>> sub
~/main/sub $ echo "Monkey patched" >> sub
~/main/sub $ git commit -am "Local hotfix"
[master 0b37958] Local hotfix
 1 file changed, 1 insertion(+)
~/main/sub $ ..
~/main $ git commit -am "Hijacked sub-module"
[master 1b3a4af] Hijacked sub-module
 1 file changed, 1 insertion(+), 1 deletion(-)

一切看起来都很美好,因为它看起来如此,但邪恶在那里。

Alice 想要在那个项目上工作,所以她克隆了主存储库(让我们假设主存储库被 Bob 推送到集线器上的某个地方)

~/main $ ..
~/test $ git clone main alice
Cloning into 'alice'...
done.
~/test $ cd alice
~/alice $ git submodule init
Submodule 'sub' (~/sub) registered for path 'sub'

到目前为止爱丽丝直到这一刻都很开心:

~/alice $ git submodule update
Cloning into '~/sub'...
done.
error: Server does not allow request for unadvertised object 0b3795831f31cf8e9c4444a021936c12210e24c6
Fetched in submodule path 'sub', but it did not contain 0b3795831f31cf8e9c4444a021936c12210e24c6. Direct fetching of that commit failed.

很明显 Git 没有在主存储库中跟踪猴子补丁,而是在子模块的本地历史记录中。

在我看来这像是 Git 子模块的设计失败,或者我只是误解了我今天要问的问题。

主要问题

有没有办法

为什么要在本地修改子模块?

我正在从事嵌入式固件开发,其中每个新项目都包括:

有时我需要猴子修补物理组件的代码库。例如,我想通过提高无线电功率来扩展蓝牙 IC 的本机范围(这不是驱动程序通常允许的功能)。

编辑

我的回答有点离谱。这里的问题是如何在不将其发布到子模块的远程存储库中的情况下在本地更改内容(因为这并不总是可能的),但仍然允许其他人使用您的更改。

您必须以某种方式将您的更改发布给您的队友。因此,更改不能仅保留在您的子模块中。以下之一将实现这一目标:

  • 分叉有问题的存储库并将分叉的存储库添加为子模块。这样,您 "own" 回购并可以修改您想要的所有内容并将更改推送到您自己的分叉远程回购。
  • 使子模块成为您自己的存储库的一部分:您只需合并 "submodule" 存储库,使其成为您自己的存储库的一部分(例如 here 中所解释的)。

旧答案

在本地修改子模块完全没问题。您必须记住的最重要的一件事是:

仅在将更改推送到子模块存储库后才将子模块更改推送到主存储库。

您的示例并未真正涵盖这一点,因为您没有推送到远程服务器。我在这里查看集中式工作流程,因为根据我的经验,这是大多数人所做的。

当您修改子模块、提交并在主存储库中提交子模块更改时,但仅推送后者,远程主存储库引用服务器端未知的子模块提交,从而产生 Server does not allow request for unadvertised object错误。当您先将子模块更改推送到子模块的存储库,然后主存储库更改时,爱丽丝只需克隆主存储库、初始化和更新子模块,然后继续您的更改。

在你的例子中

您的示例中现在的问题是您仅在 ~/main/sub 中修改了子模块,而没有在子模块存储库本身(~/sub/ 中的那个)中修改。当 Alice 从 ~/main/ 克隆时,子模块引用 ~/sub/,您更新到的提交是未知的。您必须将您的更改推送到 ~/sub/ 以使提交公开可用, 您让任何人在您的主存储库中获取更新的子模块提交之前。