每当你看到:
modified: some-name (modified content)
在git status 输出中,这意味着您的 Git 存储库认为名为@987654326@ 的目录是某个其他 Git 存储库的一部分。 (注意这里的子目录/文件夹名称,在本例中为some-name,不以斜线结尾。)
我认为有助于思考这个问题的一种方法是假装涉及多个 Git(因为有):一个是 您的 Git,在您的存储库中工作,另一个在本例是文件夹或子目录的 Git,在第二个 Git 存储库中工作。它是——或者至少曾经——在某个时候是第二个 Git 存储库。
这值得重复:这个子目录/文件夹真的是之前的某个其他 Git 存储库。它可能仍然是第二个存储库,也可能不是。此外,您自己的 Git 存储库已经以某种方式记录了其他 Git 的存在。如果您的 Git 存储库没有记录,您会看到:
Untracked files:
(use "git add <file>..." to include in what will be committed)
subgit/
然而,你看到的是:
Changes not staged for commit:
...
modified: subgit (modified content)
因此,您的 Git 存储库的git status 输出显示modified content 或untracked content 的事实意味着您的 存储库非常确定这个子目录是一些 other 存储库。您的存储库正在报告 other 存储库的相对状态。
如果你使用 git status -uall 或 git status --untracked-files=all(它们的含义相同,-u 是 --untracked-files= 的缩写),你可以获得另一个提示:如果你的 Git 没有 em> 认为子目录包含另一个 Git 存储库,它会查看子目录内部并告诉您目录中的每个文件。如果您的 Git 确信该子目录是它自己的 Git 存储库,它不会查看其中并报告每个文件——但您的 Git 也不会说“修改过的内容”或像这样的“未跟踪的内容”。
怎么做取决于你最终想要发生什么
首先,我们需要谈谈子模块。
子模块
Git 有一个名为子模块 的概念。有些人称它们为 sob-modules,因为它们让程序员哭泣。 ? 子模块本质上意味着:我告诉我的 Git 记录有关另一个 Git 存储库的信息。
现在,考虑一下克隆 Git 存储库的方式。它使用一个简单的命令:git clone <em>url name</em>,或者甚至可能只是git clone <em>url</em>,生成的克隆将进入基于 URL 的目录/文件夹名称。 (给出 name 参数会选择目录/文件夹名称,而不是让 URL 指示它。)
因此,如果您的存储库(我们将其称为 superproject,因为它监督一些子 Git)将引用某个 other 存储库,那么您的 Git需要的是:
- 它应该克隆的 URL
- 克隆时应使用的目录/文件夹名称
这两条信息在 proper 子模块中存储在超级项目顶层名为 .gitmodules 的文件中。
需要此.gitmodules 文件来创建超级项目的新 克隆。它仅在 new 克隆中需要!如果您已经有一个克隆,它正在工作并且正在管理它的子存储库,那么.gitmodules 文件的内容就不再重要了。
不管有没有.gitmodules,你的Git 也会记录更多关于子模块
每个 Git 存储库都包含三个部分:
- Git 存储
.git 存储库本身。这包含几个数据库,您不需要自己摸索。
- Git 还存储了不同的东西,称为 index、staging area 或 cache。它的名称取决于谁或 Git 的哪个部分在执行调用,但所有三个都是同一事物的名称。
- Git 为您提供了一个工作树。 Git 存储库中的文件以特殊的、冻结的、压缩的、仅限 Git 的格式存储。它们不能更改。它们可以添加到,就像添加更多版本的文件一样,但它们都只是历史记录。因此,您需要一个可以at 以普通非 Git 格式获取这些文件并对其进行处理的地方。这就是你的工作树。
工作树通常是您最常使用的 Git 的一部分——当然,它就在名称中,不是吗?然而,从某种意义上说,Git 并没有真正使用工作树。它将文件放入其中,供您使用;当您运行git add 时,您的 Git 会将工作树中的文件 out 复制到上面 #2 中的那个东西中:它们进入 index。
当 Git 进行 new 提交时,Git 会使用 index 中的文件。正是文件 in 的存在使得文件首先被跟踪。当您让 Git 填写您的工作树时,使用 git checkout 提取提交,Git 首先填写您的 index。所有真正的 Git 操作都发生在这个索引中:现有的提交进入它,新的提交从它开始。所以真正重要的是索引,而不是工作树,至少就新提交而言。无论你在工作树中做什么,Git 只真正关注索引。 这就是为什么你必须一直使用git add:它是每个文件的副本在对 Git 很重要的索引。
简而言之,您的索引包含将进入您进行的下一次提交的内容。它里面有一份所有文件的副本,采用预先 Git 化的、随时可以永久冻结的格式。这就是使您的索引如此重要的原因。如果您更改工作树文件,则必须使用 git add 复制(和 Git-ify)该文件,以便为下一次提交做好准备。
因此,对于 Git,重要的是 commits,由它们大而丑陋的哈希 ID 标识——这是唯一的:没有两个不同的提交具有相同的哈希 ID——以及 index 用于进行 new 提交。你使用工作树来完成你的工作,然后你更新你的 Git 的索引并让你的 Git 从中做出新的提交。
这也是子模块再次出现的地方。如果您的 Git 充当超级项目,例如监督某个名为 subgit/ 的文件夹中的某个子 Git,那么 您的 Git 将记录 他们的 Git 的提交哈希 ID,在你的索引!
想一想。您的 Git 不会保存他们的 Git 文件。您的 Git 知道,如果他们的 Git(您的 subgit/ 文件夹中的 Git)在提交中保存了一些文件,那么该提交哈希 ID 是好的永远。一个简单的哈希 ID,尽管它可能又大又丑,但它唯一地标识了所有这些文件,以它们永久保存的形式。所以你的 Git 不需要 来保存文件。它所要做的就是保存提交的哈希 ID。
这正是您的超级项目 Git 所做的。您的 Git 没有保存文件(他们的文件),而是将他们的提交哈希 ID 保存在您的索引中。然后,因为它在你的索引中,它也进入你的提交。 从现在开始,你所做的每个新提交都包含他们的哈希 ID。 如果你想记录一个不同的哈希 ID,你告诉他们——另一个 Git,在控制subgit/ 文件夹——切换到不同的提交哈希 ID,或进行全新的提交;然后你有 你的 Git 在你的索引中记录那个哈希 ID,这样你的 next 提交就有正确的子项目哈希 ID。
请注意,即使没有 .gitmodules 文件,所有这些都可以正常工作。 但是,如果您没有 .gitmodules 文件,那么这个监督其他 Git 存储库的超级项目可以在这里工作,但是您以后将无法轻松克隆它。
现在你必须决定:你是否想要一个子模块?
如果您想要子模块,那么,您已经在那儿了——或者至少在那儿的一半。如果您有一个列出子项目存储库的.gitmodules 文件,则您的超级项目已经为将来的克隆记录了正确的 URL 和名称。如果没有,您可能应该使用git submodule add 创建一个.gitmodules 文件。另请参阅How to `git submodule add` Existing sub Repository?(这回答了如何修复最初未正确添加但您确实想用作适当子模块的子项目存储库的问题。)
所以你想要一个子模块,并且已经在.gitmodules
鉴于您已决定确实要使用子模块,并且所有内容都已正确记录,您现在需要做的是不git add subgit。正如您已经看到的那样,这没有任何作用!
相反,您需要做的是开始一个新会话——通过cd-ing 进入子模块,或者打开一个新窗口,您可以在其中开始在子模块中工作,或其他任何方式。现在您在一个新的和不同的存储库中!但是,呃哦,你可能处于分离 HEAD 模式。
没关系:您可以选择在此模式下工作。或者,您可以运行 git checkout <em>somebranch</em> 进入某个分支,或者运行 git checkout -b <em>newbranch</em> 在当前提交处创建一个 new 分支。您应该为 这个 Git 存储库做任何有意义的事情。
现在您在这个新的不同的存储库中,您可以像往常一样使用您的工作树。一旦你按照你想要的方式排列了所有文件,在你想要的分支上,你将git add修改过的文件和/或git add未跟踪的文件将它们复制到索引中。然后你git commit 结果。现在你有了一个新的提交,你可以git push 它或者你在 this 存储库中对这些提交执行的任何操作。
现在您已经完成了包含子项目中更新和/或新文件的新提交,现在您可以返回到超级项目。你cd回到超项目,或者关闭这个窗口,或者其他什么,然后在超项目中恢复工作。
现在您在超级项目中所要做的就是运行git add <em>subproject</em>。如果子项目名为subgit,则只需git add subgit。这会将子存储库的 提交哈希 ID 复制到您的 index 中,因此现在可以提交了。添加您喜欢的任何其他内容,然后运行 git commit 进行新提交,永久冻结所有索引内容。这会保存 您的 文件和您的子项目的提交哈希 ID,所有这些都已准备好进入您的索引。
所以你根本不需要子模块或子git
这个有点难,尤其是在非常旧的 Git 版本中。现代 Git 让它变得更容易。所以在这里我将把整个答案外包给How do I remove a submodule? 请注意,大多数答案,包括this short one for modern Git,都认为您还将删除整个工作树的子存储库。如果您想保留工作树,最简单的方法就是将其移动或复制到其他地方。 请务必保存已修改和/或未跟踪的文件,因为它们可能在子存储库中的任何提交中!