Git 不会推送目录/文件夹;它甚至不推送文件。 Git 推送的是 commits,因为这是 Git 包含的内容。 Git repository 实际上是一个大型的提交数据库。每个提交都有编号,带有一个大而难看的哈希 ID,该 ID 对那个特定的提交是唯一的,1 并且您的存储库要么有一些特定的提交——它通过它的编号找到它——或者它没有那个完全承诺。所以 Git 需要知道的只是数字;这就是 Git 如何实现其分布式版本控制系统存在的“分布式”部分。你连接两个 Git,它们比较它们的数字(只是数字!)以确定谁拥有什么。
当然,提交本身做包含文件。这些文件具有完整路径,例如Data/some/name.ext、Writing/another/file 或其他。请注意,斜杠是文件名的一部分:这里没有文件夹。文件夹是你的操作系统的产物,它要求 Git 在斜线处分解它们,并创建文件夹。
当您使用文件时,通过 Git 存储库,您使用/使用的文件不是存储库中提交的文件! (那些 文件以 Git-only 格式存储,只读,压缩,并在所有提交中重复数据删除。因此,每个 提交都有一个 每个文件的完整副本 无关紧要,因为共享某个文件的相同版本的两个或多个提交实际上共享一个副本。)Git 只是复制某个文件的提交版本——嗯,整个将文件版本的快照提交到您的工作空间,供您处理/使用。这些是文件,在文件夹中,在你的工作树中。但它们不在在 Git 中:它们是从 Git 中复制出来的,如果你进行新的提交,Git 会制作它们的新副本——或通过以下方式共享现有副本它的重复数据删除过程——在一个新的提交中。
当您使用 git push 时,您选择 commits 发送到其他 Git。整个提交都进行了。如果您希望提交只包含名为 Data/some/name.ext 的文件,而不包含任何名为 Writing/another/file 的文件,则需要进行此类提交。
您可以在一个存储库中执行此操作,或者您可以通过创建多个不同的存储库来执行此操作(每个存储库都有自己独立的工作树)。在单个存储库中执行此操作非常棘手如果该存储库将也包含其中包含Writing/another/file 的提交,但可以做到。
所有这些都是导致人们在使用 Git 时实际使用的组织的原因。一般来说,一个存储库有一个工作树;一棵工作树中包含来自该存储库中提交的文件。所有这些文件都将进入该存储库中的未来提交。2每个提交都有每个文件的完整快照,即使文件没有更改。 (这就是为什么在提交中以特殊的 Git-only、not-normal-files-at-all 存储格式对它们进行重复数据删除。这也是 Git 可以存储字面上 不能如果您运行 Windows,请在您的计算机上提取 em>。3)
你提到了子模块。关于子模块需要了解的是:它们只是另一个 Git 存储库。也就是说,你可以拥有一个 Git 存储库——我们称之为 outer——你可以克隆:
git clone <url-of-outer>
cd outer # get into the clone we just made
您现在可以git checkout 使用 git clone 创建的存储库中的任何提交,它从其他回答问题的 Git 复制了所有 提交4电话/短信/任何你喜欢的类比,在给定的 URL。
同时,部分或全部提交在你刚刚创建的克隆说,实际上:现在,在sub/,让我从另一个 Git 存储库提交 a123456 . 另一个 Git 存储库的 URL 位于每个提交中的一个文件中,名为 .gitmodules,因此您的 Git 在您的系统上可以执行以下操作:
(mkdir -p sub && cd sub && git clone <url> .)
如果需要,或等效的,以创建 sub 克隆。一旦sub/ 目录存在并克隆了other Git 存储库,您在outer 中工作的Git 就可以运行:
(cd sub && git checkout a123456)
检查该提交。请注意,我们现在最多有 4 个 Git 存储库:一个位于您列出的 URL,您的位于 outer,一个位于 .gitmodules 中列出的 URL,一个位于 outer/sub/。
子模块可以很好地工作,但有几个问题。简而言之,与没有任何子模块的存储库相比,它们的工作量更大。它们曾经非常痛苦,以至于人们称它们为 sob-modules。现在情况好些了,但还不是很好。
这里最重要的是让你了解 Git 模型:repository 保存 commits:
-
您通过其哈希 ID 检查提交(通常,不过,通常使用分支名称来查找哈希 ID),现在您将快照中的文件存储在提交中。每个提交都有每个文件的完整快照。
-
您在您的工作树中使用这些文件的您的副本。
-
您使用 git add 让 Git 更新其提议的 next 提交,该提交开始与您发出的提交相匹配。 git status 命令的工作原理是将您在 Git 的索引(即暂存区域)中提议的下一次提交与您使用 git checkout 或(在 Git 2.23 或更高版本中)git switch 签出的当前提交中的内容进行比较。5 它还单独将提议的下一次提交中的内容与工作树中的内容进行比较。
-
最终,您运行git commit 从提议的下一次提交创建一个新 提交。该提交使用 index / staging-area 中的文件——其中包含 every 文件; git status 只告诉您不同 文件以保持输出较小——以制作新快照。它还添加了各种额外信息,例如您的姓名和电子邮件地址。6
-
一个新的提交更改了存储在 你的 分支名称中的哈希 ID。您的分支名称是您的,而不是其他人的。
-
一旦你有了新的提交,你就可以使用git push 将这些提交发送到其他 Git 存储库。这会将您的提交直接添加到他们的 分支。也就是说,push 要求他们更改他们的 branch 名称。
-
无论您是否有新的提交,您都使用git fetch 来获取 新的提交来自 其他 Git 存储库——通常是您克隆的那个,称为 @987654350 @。这不会影响您的分支!这只会更新您的远程跟踪名称:您的 Git 对其分支的记忆。
-
一旦您从其他人那里获得新的提交,您可能希望将它们合并到一些您的 分支中。这需要第二个命令,例如,git merge 或 git rebase。
-
这个由两个命令组成的序列——fetch,然后是 merge-or-rebase——非常常见,所以 Git 将它包装成一个 git pull 命令。如果您是初学者,我建议避免此提交,因为有些事情会出错。很难知道当它运行 两个 您还不太了解的命令时发生了什么。如果您知道您运行的是哪个实际命令,那么事情可能仍然会出错,但现在您知道要询问什么了。 (但人们仍然喜欢git pull,所以如果你喜欢,请记住:它运行两个命令。)
1此 ID 在所有 Git 存储库中是唯一的。有一些不寻常的情况,其中一个哈希 ID 号可能用于两个不同 Git 存储库中的两个不同提交,但如果是这种情况,这两个 Git 存储库将无法再相互通信,因为提交 ID 需要是独一无二的。当前,ID 被错误重复使用的几率是 2160 中的 1(最终会更高),这个几率小到可以忽略不计。
2这些未来的提交是由 Git 调用的东西进行的,不同的是,索引、暂存区或缓存,这里就不赘述了。请注意,Git 实际上并没有从工作树文件中进行新的提交。索引在某些方面与工作树相当匹配,但您必须明确地git add 文件将它们复制回工作树,到索引, 在提交之前。
如果您选择使用git commit -a,请注意,这只是意味着在提交之前运行git add 的一个版本,所以您实际上仍然在使用git add。使用commit -a 试图忽略这个索引/暂存区的存在是很诱人的,但我发现这是不明智的:Git 偶尔会设法用它的索引打你一巴掌,如果你不知道它的全部内容,你会讨厌使用 Git。
3例如,Git 可以存储一个名为 aux.h 的文件,但 Windows 不能。如果您有一个包含aux.h 的提交,并检查它,您将收到有关文件名的投诉。 Git 仍然可以处理这些提交,甚至可以进行包含更新的aux.h 的新提交,尽管这样做很棘手:正常的工作方式不(工作),因为 Windows 无法存储 aux.h文件。但 Git 可以,因为提交的文件不是文件:它们是特殊的 Git 对象,采用冻结和去重格式。
4这有点棘手,但同样重要的是要意识到:当你克隆一个存储库时,你会得到他们所有的commits,但没有一个 >分支机构。
在技术上更正确的是,您在克隆中获得了一个 分支名称。您获得的分支名称由您决定:您在 git clone 时间提供它,并带有您的 -b 参数。例如,git clone -b develop <url> 让您的 Git 创建并签出一个名为 develop 的分支。如果您不提供-b 选项,您的Git 会询问他们的Git 他们 推荐哪个分支名称,并使用该名称。通常的默认设置是让您的 Git 使用 master,如果他们已将主分支名称切换为 main(例如,较新的 GitHub 存储库),则使用 main。
与此同时,您的 Git 确实知道 它们的 分支名称。你的 Git 在 git clone 操作期间看到了它们。但是,您的 Git 对它们的分支名称所做的 是将它们变成您的 远程跟踪名称。这些名称类似于origin/master(或origin/main)、origin/develop 等。也就是说,我们只需将 origin/ 放在他们的 branch 名称前面,以使您的 remote-tracking 名称。这些名称记住它们的分支名称。
这些远程跟踪名称允许您的 Git 创建自己的分支名称从它们的名称。但是当您这样做时,该分支名称现在是 yours。他们的 Git 不能与 您的 分支混淆,因为您的 Git 不断将他们的名称更改为您的远程跟踪名称。这里还有很多要了解的内容,但入门就足够了。
5git checkout 命令有两种模式:一种“安全”模式,不会破坏工作树中未保存的工作,另一种“不安全”模式,如果您要求 Git使用它,会。不幸的是,git checkout 的一些不安全类型看起来与安全类型完全相同。这是一个糟糕的情况,因此在 Git 2.23 中,Git 人员将 checkout 命令拆分为 git switch(这只是“更安全”的部分)和 git restore,它运行“不安全”消灭我的工作,我决定把它扔掉集操作。如果你有 Git 2.23 或更高版本,明智的做法是教你的手指养成 git switch 的新习惯。 (我自己还在努力。)
6这个额外的信息是提交元数据。您不需要学习这个技术术语,但您最终需要至少了解此元数据中的一些内容。不过,我们也将把它留到以后。