如何编辑外部子模块,并将其推送到我自己的仓库?

How do I edit an external submodule, and push it to my own repo?

我正在写我的论文,在那里我扩展了另一篇论文。本文将所有代码 public 都放在了 repo X 中,他们自己也使用了一个子模块:我们称其为 Y。

对于我自己的项目,我制作了一个自己的存储库 Z,其中我将 X 作为子模块包含在内,因此将 Y 作为嵌套子模块包含在内。但是,我想更改 X 和 Y 中的代码,并且还能够将其推送到我自己的存储库中,以便我可以在多个位置使用它。我没有对 X 和 Y 的推送权限,因为它是其他人的回购协议。最好的方法是什么? 提前致谢,我迷路了:D

我尝试将我自己的 repo 推送到它的主目录中,但是它不包含我在子模块中更改的代码。当我进入子模块并在那里提交更改的代码时,我首先发出了分离头的警告。当我通过签出到一个新分支来解决这个问题时,它现在给了我:

git push --set-upstream origin new_branch
ERROR: Permission to \[repo X\] denied to \[myusername\].
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

我现在 fork 存储库 X 并更改了我的 .gitmodules 中的 url。现在子模块 Y 发生了什么?

让我们从子模块的背景开始

子模块很简单,但结果很复杂。一个“子模块”由两部分组成:一个 .gitmodules 文件和两个 Git 存储库。一个存储库称为 超级项目 ,一个称为 子模块 。超级项目在一个或多个提交中包含提交的原始哈希 ID,以检出 in 子模块。这就是整个画面 — 无论如何,直到我们开始实际工作。

假设您从克隆超级项目开始

假设你 运行:

git clone --no-recurse-submodules <url> super
cd super

您现在已在 超级项目存储库中签出一个提交,并完成了 Git、运行 宁 中的说明 这个超级项目,应该检查子模块中的提交。但是没有任何子模块。您只克隆了 一个 存储库,而不是两个。

--no-recurse-submodules 选项确保 Git 不会继续克隆子模块。无论如何,这是默认设置,但我们故意在此处明确表示,以使其显而易见,以防您更改个人设置以打开递归。

你现在必须指示 Git 完成 检出,如果你想克隆子模块并检出提交。 (如果你根本不想打扰子模块,你可以让事情保持这种状态:你不会有子模块,即使超级项目需要一个。实际的 clone-and-checkout 步骤是可选的,提供无论你在做什么 都不需要 子模块。例如,Git 的 Git 存储库包含对检查 SHA 的程序的子模块引用-1 次碰撞,但它是 可选的 并且您无需为它操心。)

要完成结帐,您现在 运行:

git submodule update --init

(如果您在克隆中使用 --recurse-submodulesgit clone 命令 运行 为您提供此命令,在 git checkout 步骤之后,步骤 61).

这里有个问题:这个git submodule update --init必须运行git clonegit clone 命令需要 URL。 URL 从哪里来?这个问题的解决方案是.gitmodules文件,这个文件必须存在于commit checked out in step 6中;该文件必须列出此时必须克隆的任何子模块的 URL(s)。

一旦这些子模块被克隆,git submodule update 命令——你现在可以在没有 --init 标志的情况下使用它——通过哈希 ID 选择一个提交,它应该 git checkout in 超级项目列出的每个子模块。 superproject Git git submodule update 命令签出的哈希 ID in 子模块存储库是存储在提交中签出的那个(再次在第 6 步中)——或者更准确地说,哈希 ID 现在位于 Git 的 索引 中(但它通过步骤 6 中的结帐进入 Git 的索引)。

现在通过 git submodule update --init 克隆的任何子模块可以 自己 通过列出提交哈希 ID 并在其中包含 .gitmodules 文件来成为超级项目。如果你使用 git submodule update--recursive 选项,它将进入每个子模块并使其轮流作为它的超级项目在 its 子模块的命令下.这就是 git clone --recurse-submodules 获取 所有 子模块的子模块的子模块的子模块的过程(无论嵌套有多深)。


1 git clone 命令基本上是 shorthand 用于 运行 六个或七个命令,除了其中一个是 Git 命令:

  1. mkdir,或您的系统用于创建新 folder/directory 的任何命令。剩下的命令是 运行 新文件夹中
  2. git init: 这将创建一个新的、完全空的存储库。
  3. git remote add:这会添加名称为 select 的遥控器,但每个人都使用默认的 origin,以及您给 [=18= 的 URL ].
  4. 您指定的任何 git config 命令(默认 none)。
  5. git fetch origin:这会从 Git 软件中复制 所有提交 ,并响应您提供的 URL。它复制 没有分支: 它们的分支变成你的 remote-tracking 名字。
  6. git checkout:这会在您的新存储库中创建一个分支,并检出提交。此步骤是可选的,因为它被 --no-checkout.
  7. 禁止
  8. git submodule update --init,如果您要求它并且在步骤 6 中检出的提交中列出了子模块。此步骤是可选的,不是默认步骤。

假设你这样开始

想象一下,而不是 只是 克隆超级项目:

git clone --no-recurse-submodules <url> super
cd super

您首先亲自克隆了超级项目本身,但随后您也亲自克隆了子模块:

git clone --no-recurse-submodules <url1> super
cd super
mkdir -p super/sub
git clone --no-recurse-submodules <url2> super/sub

现在您可以 运行 git checkoutgit switchsuper(您现在所在的位置),然后 git submodule updatesuper , 而 Git 没有 t克隆一个新的存储库。 Git 仅使用您在超级项目中创建的 现有 子模块克隆。2

当您使用此方法时,.gitmodules 文件的内容将被忽略。 因此使用此方法您不必修复任何 .gitmodules 任何超级项目中的文件,以使用不同的子模块 URL.


2您可能还希望在两个 git clone 命令之后 运行 git submodule absorbgitdirs,以便从旧的 Git1.x模型的子模块存储到新的Git2.x模型。这不是 必需的 ,这只是一个好主意,因为有些混乱“子模块可能来来去去,然后又回来”的原因。当子模块 do 消失时——例如,通过提取历史提交——结果目前非常难看:子模块有很多 user-experience 缺陷,导致许多 仍然 称它们为 sob 模块,尽管自 Git.

早期以来它们有了重大改进

后果

这一切是什么意思?好吧,假设有一些你不需要接触的现有超级项目,但是你有一个子模块,或者一个子模块of一个子模块,你 需要接触,因此,您希望人们在克隆超级项目时克隆您的子模块或您的 sub-sub-module。

真正 必须 要做的就是使您自己的 Git 存储库可访问。那些“知情者”可以小心地克隆 你的 子模块并将其放置在现有超级项目提供的上层结构中。但是如果你想让它方便其他人克隆一个超级项目(仅),并让那个超级项目的git submodule update --init克隆你的子模块,您必须更新任何 Git 存储库中的 .gitmodules 文件,作为子模块的超级项目。

假设您最初有这个结构:

super                <-- their superproject
super/sub1           <-- their submodule
super/sub1/sub2      <-- their sub-submodule

有两个 .gitmodules 文件,一个在 super/sub1/.gitmodules 中列出了可以从中克隆 sub2 的 URL,另一个在 super/.gitmodules 中列出可以从中克隆 sub1.

的 URL

因为您创建了一个 替换 sub2 的新存储库,您现在必须创建一个 替换 的新存储库sub1,其中 .gitmodules 文件 sub1 中列出了用于替换 sub2 的新 URL。但是要让超级项目 super 列出新的 URL 来替换 sub1,您现在必须创建一个 替换 [=46] 的新存储库=],其中 .gitmodules 文件 in super 列出了新的 URL 用于 sub1 替换。

换句话说,触摸 .gitmodules 文件的需求从最低的子模块“冒泡”,通过每个中间子模块,最终到达最顶层的超级项目。这是不可避免的如果你想方便别人克隆你的 bottom-level子模块。

但那是 if。子模块已经很糟糕了; horrible-ness 的另一层是什么?你可以,而不是复制其他存储库只是为了在每个存储库中更新一个 .gitmodules 文件,提供一个指令,在进行递归克隆或 update --init 之后,那些想要使用你的 sub-sub-module应该删除一个克隆并用你的替换它。

由你选择:让你不方便,让别人方便,或者让别人不方便,让你更方便。