我认为,这就是您的困惑所在:Git 是一个分布式版本控制系统,其中每个用户都可以获得每个存储库的副本。 Git 存储库主要由1 两个数据库组成,一个通常比另一个大得多:
-
较大的数据库包含提交和其他内部 Git 对象。这些对象都有编号:每个对象都有一个非常大的随机数字,用hexadecimal 表示,Git 将其称为 object ID 或更不正式地称为 hash ID .这些数字在存储库的每个副本中都是相同的:也就是说,如果您的副本有一个哈希 ID 为 9c897eef06347cc5a3eb07c3ae409970ab1052c8 的对象,而其他一些存储库有一个具有相同哈希 ID 的对象,那么这些是同一个对象。
Git 需要这些数字来访问对象。但显然很难记住9c897eef06347cc5a3eb03c3ae409970ab1052c8。这甚至与我刚才引用的数字相同吗? (不是。看看你能不能看到我改变了哪个字符。)因此,每个存储库都有第二个数据库:一个将 names 映射到哈希 ID 的表。
-
较小的数据库是这个名称表。在这里,您将找到您的分支名称、标签名称和远程跟踪名称,以及其他名称。每个名称仅映射到一个哈希 ID:这意味着您可以输入分支名称,例如 master 或 main 或 QA_6.5.3_George 并让 Git 本身 查找正确的哈希 ID。
通过使用名称而不是数字,您可以避免记住哈希 ID。但这里我们必须小心:branch name 不是 branch,除非说 branch 的人表示 branch name 。 (有关记住事物与其名称之间区别的幽默方式,请参阅the Wikipedia article on Haddock's Eyes。有关 Git 中的更多区别,请参阅What exactly do we mean by "branch"?)
当你克隆一个仓库时,你会得到所有的提交(和支持的对象),你得到根本没有分支名称。然后,克隆存储库后,您的 Git 软件将在您的存储库中创建 一个 分支名称。您可以使用此分支名称或任何远程跟踪名称来创建更多分支名称;或者您可以使用任何 Git 提交哈希 ID 创建分支名称。 hash IDs 是 Git 关心的。分支名称的存在是为了您的目的。
同样,克隆过程仅复制 commits(和其他 Git 对象)数据库,而不是 names 数据库。这意味着所有您的分支名称都是本地的。甚至您的远程跟踪名称也是本地名称:它们是您的软件和存储库本地 内存 其他存储库的 分支 名称。
当您运行git push 时,您的 Git 软件会连接到其他一些 Git 软件。您的 Git 软件从您的 Git 存储库中读取。他们的软件读取并可能写入他们的存储库。您的 Git 可以在这里看到他们的分支名称(和其他名称)以及他们的哈希 ID,并且您的 Git 可以向他们的 Git 提供 commits。
您的 Git(与您的存储库一起工作的软件)现在将向他们的 Git(他们的软件与他们的存储库一起工作)提供您所做的任何新提交,而他们还没有。这一切都通过 hash ID 起作用。因为哈希 ID 是唯一的,并且当且仅当两个存储库具有相同的实际 commits 时才匹配,所以您的 Git 可以轻松判断您拥有的某些提交是否与它们的某些提交相同有。因此,您所做的任何新 提交 都可以发送过来,而无需重新发送现有提交。
一旦您的 Git 完成发送新的提交(根据需要),您的 Git 现在要求他们的 Git 创建或更新一个its 分支名称数据库中的 its 分支名称。如果他们遵守这个礼貌的要求,你的 Git 也会创建或更新你相应的远程跟踪名称。
例如,假设您创建了一个新的分支名称george-ceaser。让我们进一步假设 他们 没有相同的分支名称。
无论您是否在此分支上进行任何新的提交,然后运行 git push origin george-ceaser 以便您的 Git 调用他们的 Git 并:
- 发送您所做的任何新提交(如果有),然后
- 要求他们在他们的 Git 中创建或更新一个名为
george-ceaser 的分支。
如果他们遵守这个礼貌的请求,他们现在有一个分支名称george-ceaser,并且您的 Git 将创建 origin/george-ceaser 以记住您刚刚让他们记住的名称 george-ceaser 下的相同哈希 ID。现在你和他们有“同一个分支”,正如人类所说的那样——但事实上,他们有他们自己的名字george-ceaser,而你有你自己的名字george-ceaser。这是两个不同的名字,只是碰巧拼写相同!
您可以随时删除您的george-ceaser。完成后,您应该将其删除。这根本不会影响他们的 george-ceaser:那个是他们的 名字。如果您希望他们删除 他们的 george-ceaser,您可以这样做:
git push origin --delete george-ceaser
向他们发送一个礼貌的请求,他们删除他们的分支名称george-ceaser。如果他们遵守这个礼貌的要求,你的 Git 什么都不会发生。如果他们拒绝这个礼貌的请求,你的 Git 什么都不会发生。
(同样,“您的 Git”是指“您的软件在您的存储库上运行”。)
时不时地运行git fetch origin --prune 是明智之举。这会让你的 Git 调用他们的 Git,列出他们所有的分支名称,然后——由于 --prune——你的 Git 将从 your 存储库中删除任何@987654345 @ 存在于您的存储库中的名称,但 他们的 分支 名称现在已经消失了。2 通常,您会知道您是否已经在他们的存储库中创建了george-ceaser,如果你在自己的存储库中删除名称george-ceaser,你会知道是否要让他们删除名称george-ceaser在他们的存储库。
你会在一段时间内发现这令人困惑。这从根本上让人类感到困惑,因为他们不能很好地处理克隆。我们认为克隆是精确的复制品,但它们不是;然后我们对副本进行更改,我们不记得我们更改了哪个副本;然后我们混淆了谁拥有什么,事情变得一团糟。请记住,Git 背后的理念是每个人都有一份副本。您对您的副本进行更改,之后您可以(但不是必须)使用git push 将您的新提交发送给一些其他 复制,然后要求他们创建或更新其分支名称之一以记住您的新提交。
在某些情况下,您最终可能会使用git push --force-with-lease 或git push --force 将git push 的最后一步从“礼貌请求”更改为“强制命令”。也就是说,不要问其他 Git 请,如果没问题,请创建或更新您的 george-ceaser 您可以发送 我命令您创建或更新您的 george-ceaser! 他们被允许拒绝命令,但他们更有可能服从命令,即使它是有害的,而不是他们服从礼貌的请求,即使它是有害的。所以在使用--force 或--force-with-lease 时要(非常)小心。当您使用礼貌请求表单时,Git 会努力不做任何有害的事情。
1大部分仓库也提供工作区,你使用的仓库会是这样的。
2我个人非常喜欢这种行为,可以将 fetch.prune 设置为 true,这样每个 git fetch 都会这样做,但在 Git 中到处都有一些尖锐的边缘,这可能会出乎意料删除一些远程跟踪名称,因此您可能只想在仔细选择的时间执行此操作,直到您对此更加熟悉为止。