让我们先从问题 2 开始,因为它对查看问题 1 很有用:
获取输出屏幕的含义是什么?
From http://XXXXXX/gitea/elm-ha/CFMS
* branch staging -> FETCH_HEAD
c97e1dbb7..c03c99691 staging -> origin/staging
实际上还有更多的输出,其中大部分以单词remote: 为前缀。 remote:-prefixed 文本是从服务器本身运行的命令输出的。这导致服务器发现他们的 Git 想要发送到您的 Git 的 59 个内部 Git 对象,以便您的 Git 获取他们拥有的一切,而您此时没有但正在要求。然后他们“打包”这些对象,实际上将其进一步压缩到大约 2 KiB 以发送,然后发送;然后你的 Git 解压缩这些数据,并将内部对象放入你的存储库中。
此时,您拥有所有新的提交,但您无法在存储库中找到新的提交。新的提交是From http://...,所以这是From 部分。这些提交是在 other Git 存储库中使用名称 stagingin 找到的其他 Git 存储库。所以你的 Git 写了一个名为FETCH_HEAD 的内部文件,事实上你给git fetch 的分支名称,即staging,对应于提交c03c99691,它现在存在于你的存储库中(但是,到目前为止,still 没有 name 可供您找到)。
last 行告诉你 Git 在你的存储库中做了什么让你能够find commit c03c99691。你自己的 Git 更新了你的名字origin/staging。你的origin/staging 用于查找提交c97e1dbb7。现在它找到了提交c03c99691。就是这样:
c97e1dbb7..c03c99691 staging -> origin/staging
意味着:他们的staging 变成了你的origin/staging,而你的origin/staging 曾经拥有 c97e1dbb7,但现在拥有c03c99691,和他们的staging 一样。
这里的最终结果是您的origin/staging,它是一个远程跟踪名称而不是一个分支名称,拥有相同的提交哈希ID 作为他们的 分支 名称 staging。因此,您可以使用名称 origin/staging 来查找提交。您也可以使用原始哈希 ID:c03c99691。
Git 真的是关于提交
在 Git 中,重要的是 提交。每个提交都有一个唯一的编号。你刚刚从他们那里得到的提交结束,提交的唯一编号是c03c99691。 (这实际上是一种缩短的形式:完整的哈希 ID 更大更丑。Git 有时会缩短它们,只保留前几个字符,以帮助避免压倒单纯的人类。) 这个数字在 每个 Git 存储库。您的存储库使用这个编号,他们的也是。
每个提交本身由两部分组成:
-
提交包含每个文件的完整快照。提交中的文件以特殊的、压缩的、只读的、仅 Git 的和重复数据删除的格式存储。这样一来,当您进行新的提交时,您主要是在重复使用 previous 提交中的文件,因此新的提交不会占用太多空间。只有 更改 文件需要新的内部对象。对于所有未更改的文件,它们的内容与其他提交中的文件相同,因此它们可以共享去重部分。
-
同时,每个提交还包含一些关于提交本身的信息,例如谁提交、何时提交以及为什么提交。在提交内的此信息中,每个提交都包含用于进行 this 提交的任何 earlier 提交的原始哈希 ID 列表。通常只有一个这样的哈希 ID,我们称之为提交的父。
这个 父哈希 ID 技巧是 Git 工作的原因。假设我们有一系列提交,所有提交都在一个简单的行中,没有进行分支和合并。此序列中的 last 提交有一些丑陋的哈希 ID,但我们将其称为提交 H。提交 H 在其自身内部有其 parent 提交的大而丑陋的哈希 ID;我们将该提交称为G。
我们说子提交指向父提交,我们可以画出:
<-G <-H
您可以看到从H 出来的箭头,指向G。当然,G 也有一个箭头。它向后指向G 之前的提交,我们称之为F:
... <-F <-G <-H
和以前一样,F 也指向后面。这个向后看的链让我们——或 Git——从 last 提交开始并找到历史。 存储库中的历史无非就是存储库中的提交。 仅此而已,但也仅此而已。每个提交都向后指向更早的提交。
分支名称和其他名称发生变化;哈希 ID 保持不变
这是分支名称进入我们图片的地方。为了find所有提交,我们需要last提交的哈希ID。以上是提交H。所以我们把 last 提交的 hash ID 放到一个名字中,比如分支名。如果分支名称master包含提交G的hash ID,分支名称staging_kei20201211包含提交H的hash ID,我们可以画成这样:
...--F--G <-- master
\
H <-- staging_kei20201211
在这里,commit H 指向之前的 commit G。名称master 也指向提交G。这意味着直到G 的提交都在两个分支上,而提交H 仅在staging_kei20201211 上。
(在 your 存储库中是否是这种情况,我不知道。请注意,我们还使用符号名称 G 和 H 来表示提交;它们的真实名称是大而丑陋的哈希 ID。要查找哈希 ID,请使用 git rev-parse:
git rev-parse master
会告诉你 master 指向的提交的真实哈希 ID,例如。)
考虑到这一点,让我们看看当您将 new 提交添加到某个分支时会发生什么。让我们从git switch master 或git checkout master 开始,这样当前分支名称 是master,当前提交 是提交G:
...--F--G <-- master (HEAD)
\
H <-- staging_kei20201211
这张图与上一张的不同之处在于,我们在名字master后面加上了特殊的名字HEAD,告诉我们哪个分支名称是当前分支。 (git branch 命令现在会将这个名称打印为绿色,而不是白色,正如您在 staging_kei20201211 中看到的那样。)
我们现在可以创建一个也指向G 的新名称,然后切换到它:
git switch -C temp-branch
得到:
...--F--G <-- master, temp-branch (HEAD)
\
H <-- staging_kei20201211
如果我们现在以通常的方式(修改文件、git add 和 git commit)进行 new 提交,我们将获得一个新的提交,并带有一个新的、唯一的哈希 ID .这个大而丑陋的哈希 ID 将是一个不在任何其他 Git 存储库中的一个(这就是为什么它们必须像它们一样大而丑陋),但我们将其称为 commit @ 987654391@,然后画成这样:
I <-- temp-branch (HEAD)
/
...--F--G <-- master
\
H <-- staging_kei20201211
注意 name temp-branch 是如何改变的:它现在指向 新提交。旧的提交仍然存在,通过G 的提交现在位于所有三个分支上。 name 已移动,但提交保持不变。我们刚刚添加了一个 new 提交,它现在是分支 temp-branch 上的 last 提交。
如果我们检查其他分支名称,例如staging_kei20201211,并删除名称temp-branch,我们会得到:
I ???
/
...--F--G <-- master
\
H <-- staging_kei20201211 (HEAD)
提交I仍然存在,但是如果你没有在任何地方保存它的提交哈希ID,它将很难找到。 Git 将在提交上挂起一段时间,以防你想要它回来,但你必须找到它的哈希 ID。如果您不以某种方式将其收回,Git 最终将完全丢弃提交 I。 (如果我们想这样做,这就是我们进行临时提交然后放弃的方式。)
Git 的 fetch 和 push 处理 散列 ID 以传输提交
虽然我们通过名称查找提交,但实际提交本身由哈希 ID 标识。要查看我们是否已经有一些提交,当我们将两个 Git 相互连接时,它们只是交换原始哈希 ID。由于这些在每个 Git 中都是唯一的,因此一个 Git 可以通过它是否具有具有相同哈希 ID 的提交来判断另一个 Git 是否具有相同的提交。如果没有,发送的 Git 只是将其发送过来。所有 Gits 编号都以相同的方式提交,1 因此接收 Git 将使用相同的随机编号。
1为了完成这项工作,Git 使用了强大的加密哈希函数。 “加密强”部分对 Git 本身来说不是必不可少的,但对其他目的很有用。当前的算法 SHA-1 不再足够强大,Git 正在转向新的哈希算法,但这是一个长期的转变,预见到许多问题;当 Git 开始进行转换时,会出现无法预料的情况。 ?
一旦获得,receiving Git 需要有一个 name
在您的情况下,您将 git fetch 运行到一个 Git,其联系 URL 以名称 origin 被记住。你的 Git 在那个 URL 调用了服务器。该服务器调用了一个连接到存储库的 Git,其中有一个分支名称 staging,其包含哈希 ID c03c99691。为简单起见,我将此提交称为K。
您的 Git 之前曾与另一个 Git 进行过通信。事实上,给定名称origin,您的 Git 可能通过将其他 Git 存储库的所有提交复制到您自己的新 Git 存储库中来启动。所以从那时起,这个提交就被添加到了origin。他们移动了他们的分支名称staging。他们可能有:
...--G <-- master
\
J <-- staging
当您执行原始 git clone 时,您可以在 您的 存储库中说:
H <-- staging_kei20201211 (HEAD)
/
...--G <-- origin/master
\
J <-- staging, origin/staging
(或者可能是具有不同结构的不同图表,但从您的git branch 名称和git fetch 输出来看,我怀疑您有一个origin/master,我知道您有一个origin/staging)。您的 origin/* 名称来自您的 Git 复制 他们的 Git 存储库的 分支 名称。
无论如何,他们现在有了一些新的提交。我猜他们有一个新的提交,而你的 git fetch 带来了那个:
H <-- staging_kei20201211 (HEAD)
/
...--G <-- origin/master
\
J <-- staging
\
K <-- origin/staging
如果这张图是准确的,J 真的是c97e1dbb7 而K 真的是c03c99691。
现在回答问题 1
我去 Visual Studio 查看我的分支 staging_kei20201211 的历史记录,但我看不到 fetch 输出中所述的提交 c03c99691,为什么?
我不知道也不使用 Visual Studio,但一般来说,要让任何 Git 查看器向您显示一些提交,您必须告诉他们最后一次此类提交的原始哈希 ID,或者给他们一个名称这允许他们使用 Git 来查找最后一次这样的提交。
正如您现在所知道的,名称origin/staging 根据定义将找到以该提交结尾的链中的最后一个提交。那是因为你的 Git 更新了你的 origin/staging 以匹配另一个 Git 正在查看的 Git 存储库中的名称 staging。
从 bash 风格的命令行:
$ git log origin/staging
会向您展示一些提交,从 c03c99691 开始,由 origin/staging 找到。命令:
$ git show origin/staging
会找到提交c03c99691,找到它的父级——可能是c97e...,但如果你刚刚从origin 获得了两个或更多提交,也许还有其他提交——然后比较两个提交中的快照。对于每个完全相同的文件,git show 不会显示任何内容。对于每个不同的文件,git show 会告诉您发生了什么变化。所有这些都将以提交c03c99691 中的日志消息 为前缀,以及存储在提交c03c99691 中的名称和电子邮件地址以及日期和时间戳信息。