首页 > 解决方案 > 将子文件夹推送到 GitHub,而不是父目录

问题描述

我有一个名为“Project”的父目录,其中包含两个子文件夹“Data”和“Writing”。我git在“项目”上有一个目录,并希望将push“数据”文件夹添加到私人 GitHub 存储库。我有一个到 GitHub 的 SSH 连接。

  1. 如何git push将“Data”文件夹放到 GitHub 上(同时将“Writing”文件夹保留在我的本地计算机上)?
  2. 我应该git为“数据”文件夹创建另一个目录并将git remote其添加到 GitHub 存储库吗?还是子模块更好?

我将不胜感激各个步骤。谢谢你。

标签: gitgithubgit-submodulessubdirectory

解决方案


Git 不推送目录/文件夹;它甚至不推送文件。Git 推送的是提交,因为这是 Git 包含的内容。Git存储库实际上是一个大型的提交数据库。每个提交都有编号,带有一个大而丑陋的哈希 ID,该 ID 对那个特定的提交来说是唯一的,1并且您的存储库要么有一些特定的提交——它通过它的编号找到——要么根本没有那个提交。所以 Git 需要知道的只是数字;这就是 Git 如何实现其分布式版本控制系统存在的“分布式”部分。你连接两个 Git,他们比较他们的数字(只是数字!)以确定谁拥有什么。

当然,提交本身确实包含文件。这些文件有完整的路径——例如,,Data/some/name.extWriting/another/file其他。请注意,斜杠是文件名的一部分:这里没有文件夹。文件夹是你的操作系统的产物,它要求 Git 在斜线处分解它们,并创建文件夹。

当您使用通过Git 存储库获得的文件时,您处理/使用的文件不是存储库中提交的文件!(这些文件以仅 Git 格式存储,在所有提交中都是只读、压缩和重复数据删除。因此,每个提交都有每个文件的完整副本这一事实并不重要,因为两个或多个提交共享某个文件的相同版本实际上共享一个副本。)Git 只是将某个文件的提交版本(嗯,文件版本的整个提交快照)复制到您的工作空间中,供您处理/使用。那些文件夹中的文件,工作树中的文件。但它们不在Git 中:它们是从 Git 中复制出来的,如果您进行新的提交,Git 将在新提交中制作它们的新副本——或通过其重复数据删除过程共享现有副本。

使用 时git push,您选择提交以发送到其他 Git。整个提交都进行了。如果您希望提交只包含名为 的文件Data/some/name.ext,而不包含任何名为 的文件Writing/another/file,则需要进行此类提交。

可以在一个存储库中执行此操作,或者您可以通过创建多个不同的存储库来实现(每个存储库都有自己独立的工作树)。如果该存储库中包含提交,那么在单个存储库中执行此操作非常棘手Writing/another/file,但可以完成。

所有这些都是导致人们在使用 Git 时实际使用的组织的原因。一般来说,一个存储库有一个工作树;一棵工作树中包含来自该存储库中提交的文件。所有这些文件都将进入该存储库中的未来提交。2每个提交都有每个文件 的完整快照,即使文件没有更改。(这就是为什么它们被重复数据删除的原因,在提交中的特殊 Git-only、not-normal-files-at-all 存储格式。这也是 Git 如何存储实际上无法在您的计算机上提取的文件的方式, 如果你运行Windows。3 )

你提到子模块。关于子模块需要了解的是:它们只是另一个 Git 存储库。也就是说,你可以有一个 Git 存储库——我们称之为它outer——你可以克隆:

git clone <url-of-outer>
cd outer                  # get into the clone we just made

现在,您可以git checkout使用您创建的存储库中的任何提交,git clone它从给定 URL 处的其他 Git 中复制了所有提交4,这些 Git 可以接听电话/短信/任何您喜欢的类比。

同时,您刚刚创建的克隆中的部分或全部提交实际上是:现在在 中sub/,让我a123456从另一个 Git 存储库提交。 该其他 Git 存储库的URL位于每个提交中的一个名为 的文件中.gitmodules,因此您的 Git 在您的系统上可以执行以下操作:

(mkdir -p sub && cd sub && git clone <url> .)

或根据需要等效,以创建sub克隆。一旦sub/目录存在并克隆了其他Git 存储库,您的 Gitouter就可以运行:

(cd sub && git checkout a123456)

签出该提交。请注意,我们现在最多有 4 个 Git 存储库:一个位于您列出的 URL 中,您的位于 中outer,一个位于 中列出的 URL 中.gitmodules,一个位于outer/sub/.

子模块可以很好地工作,但有几个陷阱。简而言之,与没有任何子模块的存储库相比,它们的工作量更大。它们曾经非常痛苦,以至于人们将它们称为sob模块。现在情况好多了,但还不是很好。

这里最重要的是让你了解 Git 模型:存储库包含提交:

  • 您可以通过其哈希 ID 来检查提交(通常,通常使用分支名称来查找哈希 ID),现在您将快照中的文件存储在提交中。每个提交都有每个文件的完整快照。

  • 工作树中使用这些文件副本。

  • git add曾经让 Git 更新它提议的下一个提交——它开始匹配你得到的提交。该命令通过将您在 Git 的索引(即暂存区域)中提议的下一次提交与您使用或(在 Git 2.23 或更高版本中)git status签出的当前提交中的内容进行比较来工作。5 它还单独将建议的下一次提交中的内容与工作树中的内容进行比较。git checkoutgit switch

  • 最终,您运行git commit以从提议的下一个提交创建一个提交。该提交使用 index / staging-area 中的文件——其中包含每个文件;git status只告诉你不同的文件以保持输出小——制作新的快照。它还添加了各种额外信息,例如您的姓名和电子邮件地址。6

  • 新的提交会更改存储在您的分支名称中的哈希 ID。您的分支名称是的,而不是其他人的。

  • 一旦你有了新的提交,你git push就可以将这些提交发送到其他 Git 存储库。这会将您的提交直接添加到他们的 分支。也就是说,push 要求他们更改他们的分支名称。

  • 无论您是否有新提交,您都可以git fetch其他Git存储库(通常是您克隆的存储库,称为origin. 这不会影响您的分支!这只会更新您的远程跟踪名称:您的 Git 对其分支的记忆。

  • 一旦您从其他人那里获得新的提交,您可能希望将它们合并到您的某些分支中。这需要第二个命令,例如,git mergegit rebase

  • 这个包含两个命令的序列——fetch,然后是 merge-or-rebase——非常常见,所以 Git 将它包装成一个git pull命令。如果您是初学者,我建议避免此提交,因为有些事情会出错。当它运行两个你还不太了解的命令时,很难知道发生了什么。如果你知道你运行了哪个实际命令,事情可能仍然会出错,但现在你知道要什么了。(但人们仍然喜欢git pull,所以如果你喜欢,请记住:它运行两个命令。)


1此 ID 在所有 Git 存储库中都是唯一的。有一些不寻常的情况,其中一个哈希 ID 号可能用于两个不同 Git 存储库中的两个不同提交,但如果是这种情况,这两个 Git 存储库将无法再相互通信,因为提交 ID需要要独一无二。当前,ID 被错误地重复使用的几率是 2 160 分之1 (最终会更高),这个几率小到可以忽略不计。

2这些未来的提交是由 Git 调用的东西组成的,不同的是indexstaging areacache,但我不会在这里详细介绍。请注意,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这有点棘手,但同样重要的是要意识到:当你克隆一个存储库时,你会得到他们的所有提交,但没有他们的分支

在技​​术上更正确的是,您在克隆中获得了一个分支名称。您获得的分支名称取决于您:您git clone有时会使用您的-b参数提供它。例如,git clone -b develop <url>让您的 Git 创建并签出一个名为develop. 如果您提供-b选项,您的 Git 会询问他们的 Git他们推荐的分支名称,并使用该名称。通常的默认设置是让您的 Git 使用master,或者main如果他们已将其主分支名称切换为main(例如,较新的 GitHub 存储库)。

同时,您的 Git 确实知道它们的 分支名称。你的 Git 在操作过程中看到了它们git clone。但是,您的 Git它们的分支名称所做的是将它们转换为您的远程跟踪名称。这些是origin/master(或origin/main)、等名称origin/develop。也就是说,我们只是在他们的分支origin/名称前面加上这个词,以使您的远程跟踪名称。这些名称会为您记住它们的分支名称。

这些远程跟踪名称允许您的 Git它们的名称中创建您自己的分支名称。但是当你这样做时,那个分支名称现在就是的了。他们的 Git 不能弄乱你的分支,因为你的 Git 不断地把他们的名字改成你的远程跟踪名字。这里还有很多要知道的,但是这已经足够开始了。

5git checkout命令有两种模式:一种“安全”模式,它不会破坏工作树中未保存的工作,另一种“不安全”模式,如果您要求 Git 使用它,它将. 不幸的是,一些不安全的类型git checkout看起来与安全的类型完全相同。这是一个糟糕的情况,因此在 Git 2.23 中,Git 人员将checkout命令拆分为git switch- 这只是“更安全”的部分 - 并且git restore运行“不安全”的部分请清除我的工作,我决定扔掉它出一套操作。如果您有 Git 2.23 或更高版本,明智的做法是教您的手指养成新git switch习惯。(我自己还在做这个。)

6这个额外的信息是提交元数据。您不需要学习这个技术术语,但您最终需要至少了解此元数据中的一些内容。不过,我们也会把它留到以后。


推荐阅读