如何解决将子文件夹推送到 GitHub,而不是父目录
我有一个名为“Project”的父目录,其中包含两个子文件夹“Data”和“Writing”。我在“项目”上有一个 git
目录,我想将“Data”文件夹push
放到一个私有的 GitHub 存储库中。我有一个到 GitHub 的 SSH 连接。
- 我如何
git push
只将“Data”文件夹放到 GitHub 上(同时将“Writing”文件夹保留在我的本地计算机上)? - 我是否应该为“Data”文件夹创建另一个
git
目录并将其git remote
放到 GitHub 存储库中?还是子模块更好?
如果您提供个别步骤,我将不胜感激。谢谢。
解决方法
Git 不推送目录/文件夹;它甚至不推送文件。 Git 推送的是提交,因为这是 Git 包含的内容。 Git 存储库 实际上是一个大型提交数据库。每个提交都有编号,有一个丑陋的大哈希 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 可以存储实际上不能的文件的方式em> 在您的计算机上解压缩(如果您运行的是 Windows)。3)
你提到了子模块。关于子模块需要了解的是:它们只是另一个 Git 存储库。也就是说,您可以拥有一个您克隆的 Git 存储库(我们称之为 outer
):
git clone <url-of-outer>
cd outer # get into the clone we just made
您现在可以 git checkout
使用 git clone
创建的存储库中的任何提交,它复制了其他一些提交4 Git 在给定的 URL 上接听电话/短信/任何你喜欢的类比。
与此同时,部分或全部提交在你刚刚创建的克隆中说,实际上:现在,在sub/
中,让我从另一个 Git 存储库。 另一个 Git 存储库的 URL 位于每个提交中的一个名为 a123456
的文件中,因此您的系统上的 Git 可以执行以下操作:
.gitmodules
或等价物(如果需要),以创建 (mkdir -p sub && cd sub && git clone <url> .)
克隆。一旦 sub
目录存在并克隆了 other Git 存储库,您在 sub/
中运行的 Git 就可以运行:
outer
检出该提交。请注意,我们现在最多有 4 个 Git 存储库:一个在您列出的 URL 中,您的在 (cd sub && git checkout a123456)
中,一个在 outer
中列出的 URL 中,一个在 .gitmodules
中。>
子模块可以很好地工作,但有几个问题。简而言之,它们比没有任何子模块的存储库更重要。它们曾经非常痛苦,以至于人们称它们为 sob 模块。现在情况好多了,但仍然不是很好。
这里最重要的是围绕 Git 模型:一个 存储库 保存 提交:
-
您通过哈希 ID 检查提交——不过,通常使用分支名称来查找哈希 ID——现在您将快照中的文件存储在提交中.每个提交都有每个文件的完整快照。
-
您在您的工作树中使用这些文件的副本。
-
您使用
outer/sub/
来让 Git 更新其提议的 next 提交——它开始匹配您得到的提交。git add
命令的工作原理是将您在 Git 索引(即暂存区)中提议的下一次提交与您使用git status
或(在 Git 2.23 或更高版本中)检出的当前提交中的内容进行比较 {{1} }.5 它还单独比较下一次提交建议中的内容与您的工作树中的内容。 -
最终,您运行
git checkout
从提议的下一次提交创建一个新提交。该提交使用索引/暂存区中的文件——它充满了每个文件;git switch
仅告诉您关于不同 文件以保持输出较小 — 以制作新快照。它还添加了各种额外信息,例如您的姓名和电子邮件地址。6 -
一个新的提交改变了存储在您的分支名称中的哈希 ID。你的分支名称是你的,不是别人的。
-
一旦您有新的提交,您就可以使用
git commit
将这些提交发送到某个其他 Git 存储库。这会将您的提交直接添加到他们的 分支。也就是说,推送要求他们更改他们的分支名称。 -
无论您是否有新的提交,您都可以使用
git status
来获取 新的提交来自某个其他 Git 存储库——通常是您克隆,称为git push
。这不会影响您的分支!这只会更新您的远程跟踪名称:您的 Git 对其分支的记忆。 -
一旦您从其他人那里获得新的提交,您可能希望将它们合并到您 的一些分支中。这需要第二个命令,例如
git fetch
或origin
。 -
这个由两个命令组成的序列——获取,然后是合并或变基——非常常见,所以 Git 将它包装成一个
git merge
命令。如果您是初学者,我建议避免这个提交,因为有些事情会出错。很难知道当它运行两个你还不真正理解的命令时发生了什么。如果您知道实际运行的是哪个命令,那么事情可能仍然会出错,但现在您知道要询问什么。 (但人们仍然喜欢git rebase
,所以如果你喜欢,请记住:它运行两个命令。)
1此 ID 在所有 Git 存储库中是唯一的。在一些不寻常的情况下,一个哈希 ID 号可能用于两个不同 Git 存储库中的两个不同提交,但如果是这种情况,这两个 Git 存储库将无法再相互通信,因为提交 ID 需要是独一无二的。在当前时间,ID 被错误使用的几率是 2160 中的 1(并且最终会更高),这小到可以忽略。
2这些未来的提交由 Git 调用的东西组成,例如索引、暂存区或缓存,但我不会在这里详细介绍。请注意,Git 实际上并没有从工作树文件中进行新的提交。索引在某些方面与工作树很好地配对,但是您必须显式地将 git pull
文件复制回从工作树,到提交之前的索引。
如果您选择使用 git pull
,请注意这仅意味着 在进行提交之前运行 git add
的一个版本,因此您实际上仍在使用 {{1 }}。使用 git commit -a
试图忽略这个索引/暂存区的存在很诱人,但我发现这是不明智的:Git 偶尔会设法用它的索引,如果你不知道它是什么,你会讨厌使用 Git。
3例如,Git 可以存储名为 git add
的文件,但 Windows 不能。如果您有一个包含 git add
的提交,并检查它,您将收到关于文件名的投诉。 Git 仍然可以处理这些提交,甚至可以进行包含更新的 commit -a
的新提交,尽管这样做很棘手:正常的工作方式并不(工作),因为 Windows 无法存储 { {1}} 个文件。但是 Git 可以,因为提交的文件不是文件:它们是特殊的 Git 对象,采用冻结和重复数据删除的格式。
4这有点棘手,但认识到这一点也很重要:当你克隆一个存储库时,你会得到他们所有的提交,但没有他们的 >分支机构。
技术上更正确的是,您会在克隆中获得一个分支名称。您获得的分支名称由您决定:您可以在 aux.h
时间将 aux.h
参数提供给它。例如,aux.h
让您的 Git 创建并签出名为 aux.h
的分支。如果您不提供 git clone
选项,您的 Git 会询问他们的 Git 他们推荐哪个分支名称,并使用该名称。通常默认情况下,您的 Git 使用 -b
或 git clone -b develop <url>
,如果他们已将其主分支名称切换为 develop
(例如,较新的 GitHub 存储库)。
与此同时,您的 Git 确实知道他们分支的名称。您的 Git 在 -b
操作期间看到了所有这些。但是,您的 Git 对它们的分支名称所做的是将它们转换为您的远程跟踪名称。这些名称类似于 master
(或 main
)、main
等。也就是说,我们只需在他们的分支名称前面加上 git clone
字样,就可以使您的远程跟踪名称。这些名称记住它们的分支名称。
这些远程跟踪名称允许您的 Git 从它们的名称创建自己的分支名称。但是当您这样做时,该分支名称现在是您的。他们的 Git 不能弄乱您的 分支,因为您的 Git 不断将其名称更改为您的远程跟踪名称。这里还有很多东西要了解,但这足以让您入门。
5origin/master
命令有两种模式:一种“安全”模式,不会破坏工作树中未保存的工作,另一种“不安全”模式,如果你问Git 使用它,将。不幸的是,一些不安全的 origin/main
看起来与安全的完全一样。这是一个糟糕的情况,因此在 Git 2.23 中,Git 人员将 origin/develop
命令拆分为 origin/
(这只是“更安全”的部分)和 git checkout
,后者运行“不安全”请清除我的工作,我决定将其扔掉套操作。如果您有 Git 2.23 或更高版本,那么教您的手指养成新的 git checkout
习惯是明智之举。 (我自己仍在努力。)
6这个额外的信息是提交元数据。您不需要学习这个技术术语,但您最终需要至少了解此元数据中的一些内容。不过,我们也将其留待以后使用。
,我是否应该为“Data”文件夹创建另一个 git 目录并将其远程 git 远程到 GitHub 存储库?
还是子模块更好?
实际上,即使您将 Data 作为子模块引用,这仍然意味着 Data 是它自己的存储库。
是的:
- 使用
git-filter-repo
拆分Data
(您可以在对原始存储库进行任何重命名/移动后执行此操作:请参阅“Git Split Repository directory preserving move / renames history”) - 然后,如果需要,在主存储库中将新存储库声明为子模块(路径
/Data
)。