2

最近我用这个命令更新了我的 vim 配置库中的子模块:

git submodule update --recursive --remote

当我打电话时,git status我得到了这个:

On branch master
Your branch is ahead of 'origin/master' by 5 commits.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
  (commit or discard the untracked or modified content in submodules)

        modified:   .vim/pack/starter-pack/start/YouCompleteMe (modified content)

no changes added to commit (use "git add" and/or "git commit -a")

然后我跟踪了具有“修改内容”的子模块链,发现唯一的修改是子模块的未跟踪提交:

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   vendor/bottle (new commits)
        modified:   vendor/jedi (new commits)
        modified:   vendor/waitress (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

这些子模块(bottle、jedi、waitress)的主分支位于其远程来源的主分支之后,所以我认为git submodule update所做的不仅仅是拉取每个 repo 的来源,而是找到父存储库所需的适当版本。

(new commits)如果它是父模块需要的确切提交,为什么甚至 git 会标记这个 repos ?那里发生了什么?

4

1 回答 1

2

这里发生的事情是您的超级项目现在不一致。具体来说,您的超级项目有需要提交的gitlink 。

您应该添加新的 gitlinks(git add像往常一样使用 )和提交(像往常一样)。然后你可以推送你的新提交(像往常一样)。

子模块意味着超级项目

模块只是一个被另一个 Git 存储库直接使用的 Git 存储库。在这种情况下,子模块本身只是一个普通的 Git 存储库:它不知道另一个 Git 存储库。另一个存储库是我们称为superproject的存储库,它确实知道子模块。

任何 Git 存储库都需要一个.git包含一些数据的目录

通常,您创建 Git 存储库的方式是从其他地方克隆它:

git clone http://...

管他呢。或者,您可能git init在目录中运行。无论哪种方式,您最终都会得到一个.git包含 Git 存储库本身的目录。在这种情况下.git,您通常会定义一个名为origin. 这是一个短名称(特别是名称origin!),它记录了一个 URL,即您在git clone上面提供的 URL。这个 URL 甚至可以指向你自己在 GitHub 上的存储库。

(如果您从其他人的存储库开始,然后决定在 GitHub 上创建自己的存储库,您甚至可能有两个遥控器。通常您会命名自己的存储库origin和另一个upstream,但就 Git 本身而言,这些只是任意的名称。我们都同意的唯一原因origin是,这是git clone我们第一次运行时为我们创建的名称。)git clone url

无论如何,.git目录中的数据包括以下内容:

  • 的网址origin
  • 任何分支的名称,以及这些分支标识的提交哈希 ID。
  • 同样,标签的名称及其提交。
  • 当前提交:在存储库中签出究竟是什么?这可能是一个分支名称,在这种情况下存储库在一个分支上,或者它可能是一个原始提交哈希 ID,在这种情况下,存储库处于“分离 HEAD”模式。

如果超级项目创建子模块怎么办?

一个超级项目需要知道关于它的每个子模块的几件事。首先,超级项目有一个名为.gitmodules. 在此.gitmodules文件中,您将找到每个子模块的URL。您还将找到每个子模块的路径

该文件的确切形式和内容在gitmodules 文档中进行了描述。稍微引用一​​下,假设它说:

[submodule "libfoo"]
        path = include/foo
        url = git://foo.com/git/lib.git

这意味着,当您克隆超级项目,然后运行git submodule init时,您的 Git 将知道它应该运行git clone git://foo.com/git/lib.git——即url部分——克隆进入include/foo目录:路径部分。

这个难题缺少一个关键部分。在您的 Git 将另一个 Git 克隆到 之后include/foo在子模块中签出什么提交?

在大多数普通存储库中,这并不是什么大问题。签出什么提交?我不知道,我只是跑git checkout master,对吧?这让我得到了 branch 上的最新提交master,这就是我想要的。

超级项目和子模块不是这样工作的。当我使用我的超级项目中的子模块时,我会围绕子模块中的一个特定提交构建我的超级项目代码。例如,我可能特别依赖于v3.4.1其他人的库,所以我会进入子项目并运行git checkout v3.4.1以检查那个特定的tag

理想情况下,我可能会让我的超级项目记录该标签(这很好,gitlinks应该允许这样做,但目前他们不允许这样做)。1 但是,在 Git 中,标签实际上只是一个特定提交的人类可读名称。标签v3.4.1可能是提交的名称feeddadac0ffee...或类似名称。那——又大又丑的哈希 ID——实际上是进入 gitlink 的。

gitlink 本身存储在每个提交中,就像一个常规文件存储在每个提交中一样。如果我在超级项目中使用新的或修改过的README文件进行新的提交,则新版本的README进入 Git 存储库,新的提交引用新的README. 之后的每个提交都继续引用新的README.

gitlink 也是如此:如果 myinclude/foov3.4.1的是子模块的哈希 ID,那么从这里开始的每个提交都有一个 gitlink 条目,上面写着:“当你签出这个提交时,你还应该进入include/foo子模块并签出哈希身份证feeddadac0ffee...”。


1如果有人想尝试添加它,我认为有一种方法可以做到,甚至可能有点向后兼容:像往常一样存储原始哈希 ID,后跟 NUL 字节,然后是引用名称。不理解新型 gitlink 的较旧 Git 可以直接使用哈希 ID,而较新的 Git 可以检测并使用该名称。Gitlink 条目无论如何都需要在从 SHA-1 到 Git 将来使用的任何哈希转换中进行类似的更改,所以这可能是添加它的好时机。


如果子模块的所有者发布了新版本怎么办?

所以,我已经测试了我的超级v3.4.1项目并且一切正常。伟大的!但是现在负责这个include/foo库的人已经更新了他们的代码并发布了版本v3.4.2。这个新版本有一些新功能,我想使用它。

我,作为超级项目的所有者,现在应该进入我的子模块git fetch,然后git checkout v3.4.2. feeddadac0ffee(这可能是哈希 ID而不是deadcabbadcab005e...。)然后我应该返回我的超级项目,进行使用新子模块所需的任何更改,测试所有内容并提交。

当我对使用子模块进行新的提交时v3.4.2,我不应该只提交我的更改。我还需要更新我的gitlink。由于我已经在子模块中完成了git checkout deadcabbadcab005e——或者git checkout v3.4.2,实际上是完全相同的事情,所以我所要做的就是git add include/foo在我的超级项目中。这会将更新后的 gitlink 添加到我的索引中,因此当我运行时git commit,我会记录的gitlink 以及我的其他更改。

这是一个新的提交,我现在可以推送我的提交,如果还有其他地方我也保留我的超级项目(在 GitHub 或其他地方)。

于 2017-07-01T16:17:25.460 回答