git 要求提交子模块修改的内容
git asks to commit submodule modified content
最近我用这个命令更新了我的 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")
然后我跟踪了具有 "modified content" 的子模块链,发现唯一的修改是子模块的未跟踪提交:
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)的 master 分支位于它们远程源的 master 分支后面,所以我想 git submodule update
所做的不仅仅是提取每个 repo 的源,而是找到合适的版本父存储库需要。
为什么即使 git 也用 (new commits)
标记这个 repos,如果它是父模块需要的确切提交?那里发生了什么?
这里发生的事情是您的超级项目现在不一致。具体来说,您的超级项目有 gitlinks 需要提交。
您应该像往常一样添加新的 gitlinks(使用 git add
)并提交(像往常一样)。然后你可以推送你的新提交(像往常一样)。
子模块隐含超级项目
一个 子模块 只是一个 Git 存储库,它被另一个 Git 存储库直接使用。在这种情况下,子模块本身只是一个普通的 Git 存储库:它不知道另一个 Git 存储库。另一个存储库是我们称为 超级项目 的存储库,它 确实 知道子模块。
任何 Git 存储库都需要一个包含一些数据的 .git
目录
通常,创建 Git 存储库的方法是从其他地方克隆它:
git clone http://...
或其他。或者,您可以在目录中 运行 git init
。无论哪种方式,您最终都会得到一个包含 Git 存储库本身的 .git
目录。在此 .git
中,您通常会定义一个名为 origin
的 remote。这是一个简称origin
!的名字,记录了一个URL,也就是你上面给git clone
的URL。这个 URL 甚至可以指向您自己的存储库,例如 GitHub。
(如果您从其他人的存储库开始,然后决定在 GitHub 上创建自己的存储库,您甚至可能有 两个 遥控器。通常您会命名您的自己的存储库 origin
和另一个 upstream
,但就 Git 本身而言,这些只是任意名称。我们都同意 origin
的唯一原因是这是 git clone
为我们创建的名称,当我们首先 运行 git 克隆 <em>url</em>
.)
无论如何,.git
目录中的数据包括以下内容:
origin
的 URL。
- 任何分支的名称,以及这些分支标识的提交哈希 ID。
- 同样,标签名称及其提交。
- 当前 提交:在存储库中签出的具体内容是什么?这可能是 分支名称 ,在这种情况下存储库在 分支 上,或者它可能是原始提交哈希 ID,在这种情况下存储库处于 "detached HEAD" 模式。
如果超级项目创建子模块怎么办?
一个超级项目需要知道关于它的每个子模块的一些事情。首先,超级项目有一个名为 .gitmodules
的文件。在这个 .gitmodules
文件中,您会找到每个子模块的 URL。您还会为每个子模块找到一个 path。
此文件的确切格式和内容在 the gitmodules documentation 中进行了描述。稍微引用一下,假设它说:
[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
目录:path 部分。
这个拼图缺少一个关键部分。在您的 Git 将另一个 Git 克隆到 include/foo
之后, 在子模块中签出了哪些提交?
在大多数普通存储库中,这不是什么大问题。检出什么提交?我不知道,我只是 运行 git checkout master
,对吧?这让我在分支 master
上得到了 latest 提交,这就是我想要的。
超级项目和子模块不是这样工作的。当我使用我的超级项目的子模块时,我围绕子模块中的 一个特定提交 构建我的超级项目代码。例如,我可能特别依赖于其他人的库的 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:如果我的 include/foo
引用子模块的 v3.4.1
的哈希 ID,则从这里开始的每个提交都有一个gitlink 条目说:"when you check out this commit, you should also go into the include/foo
submodule and check out hash ID 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...
。)然后我应该 return 到我的超级项目,进行任何需要的更改 use 新的子模块,测试所有内容,然后提交。
当我使用子模块的 v3.4.2
进行新提交时,我不应该只提交 my 更改。我还需要更新我的 gitlink。因为我已经在子模块中完成了 git checkout deadcabbadcab005e
或 git checkout v3.4.2
,这是完全相同的事情,所以我所要做的就是在我的超级项目中 git add include/foo
。这会将更新后的 gitlink 添加到我的索引中,这样当我 运行 git commit
时,我会记录 new gitlink 以及我的其他更改。
这是一个新的提交,我现在可以推送我的提交,如果有其他地方我也保留我的超级项目(在 GitHub 或其他地方)。
最近我用这个命令更新了我的 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")
然后我跟踪了具有 "modified content" 的子模块链,发现唯一的修改是子模块的未跟踪提交:
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)的 master 分支位于它们远程源的 master 分支后面,所以我想 git submodule update
所做的不仅仅是提取每个 repo 的源,而是找到合适的版本父存储库需要。
为什么即使 git 也用 (new commits)
标记这个 repos,如果它是父模块需要的确切提交?那里发生了什么?
这里发生的事情是您的超级项目现在不一致。具体来说,您的超级项目有 gitlinks 需要提交。
您应该像往常一样添加新的 gitlinks(使用 git add
)并提交(像往常一样)。然后你可以推送你的新提交(像往常一样)。
子模块隐含超级项目
一个 子模块 只是一个 Git 存储库,它被另一个 Git 存储库直接使用。在这种情况下,子模块本身只是一个普通的 Git 存储库:它不知道另一个 Git 存储库。另一个存储库是我们称为 超级项目 的存储库,它 确实 知道子模块。
任何 Git 存储库都需要一个包含一些数据的 .git
目录
通常,创建 Git 存储库的方法是从其他地方克隆它:
git clone http://...
或其他。或者,您可以在目录中 运行 git init
。无论哪种方式,您最终都会得到一个包含 Git 存储库本身的 .git
目录。在此 .git
中,您通常会定义一个名为 origin
的 remote。这是一个简称origin
!的名字,记录了一个URL,也就是你上面给git clone
的URL。这个 URL 甚至可以指向您自己的存储库,例如 GitHub。
(如果您从其他人的存储库开始,然后决定在 GitHub 上创建自己的存储库,您甚至可能有 两个 遥控器。通常您会命名您的自己的存储库 origin
和另一个 upstream
,但就 Git 本身而言,这些只是任意名称。我们都同意 origin
的唯一原因是这是 git clone
为我们创建的名称,当我们首先 运行 git 克隆 <em>url</em>
.)
无论如何,.git
目录中的数据包括以下内容:
origin
的 URL。- 任何分支的名称,以及这些分支标识的提交哈希 ID。
- 同样,标签名称及其提交。
- 当前 提交:在存储库中签出的具体内容是什么?这可能是 分支名称 ,在这种情况下存储库在 分支 上,或者它可能是原始提交哈希 ID,在这种情况下存储库处于 "detached HEAD" 模式。
如果超级项目创建子模块怎么办?
一个超级项目需要知道关于它的每个子模块的一些事情。首先,超级项目有一个名为 .gitmodules
的文件。在这个 .gitmodules
文件中,您会找到每个子模块的 URL。您还会为每个子模块找到一个 path。
此文件的确切格式和内容在 the gitmodules documentation 中进行了描述。稍微引用一下,假设它说:
[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
目录:path 部分。
这个拼图缺少一个关键部分。在您的 Git 将另一个 Git 克隆到 include/foo
之后, 在子模块中签出了哪些提交?
在大多数普通存储库中,这不是什么大问题。检出什么提交?我不知道,我只是 运行 git checkout master
,对吧?这让我在分支 master
上得到了 latest 提交,这就是我想要的。
超级项目和子模块不是这样工作的。当我使用我的超级项目的子模块时,我围绕子模块中的 一个特定提交 构建我的超级项目代码。例如,我可能特别依赖于其他人的库的 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:如果我的 include/foo
引用子模块的 v3.4.1
的哈希 ID,则从这里开始的每个提交都有一个gitlink 条目说:"when you check out this commit, you should also go into the include/foo
submodule and check out hash ID 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...
。)然后我应该 return 到我的超级项目,进行任何需要的更改 use 新的子模块,测试所有内容,然后提交。
当我使用子模块的 v3.4.2
进行新提交时,我不应该只提交 my 更改。我还需要更新我的 gitlink。因为我已经在子模块中完成了 git checkout deadcabbadcab005e
或 git checkout v3.4.2
,这是完全相同的事情,所以我所要做的就是在我的超级项目中 git add include/foo
。这会将更新后的 gitlink 添加到我的索引中,这样当我 运行 git commit
时,我会记录 new gitlink 以及我的其他更改。
这是一个新的提交,我现在可以推送我的提交,如果有其他地方我也保留我的超级项目(在 GitHub 或其他地方)。