TL;DR
您需要先运行git fetch origin,然后才能运行git checkout TLA1。
长
你走在正确的轨道上,但有很多东西要知道——还有很多人们发现的错误的东西,你应该小心。
要忘却的事情
在熟练使用 Git 之前,您需要un-学习一些东西。这些虚假声明如下:
-
“分支很重要”:这没有错,但这也不对。第一个问题是 branch 这个词,它在 Git 中基本上是模棱两可的。如果我们坚持使用两个词的短语 branch name,我们会得到更有用的东西。分支名称很重要,但仅限于人类。 Git 使用它们来帮助我们找到提交;真正重要的是提交。
-
“远程分支”:这个两个词的短语,如果有的话,比“分支”这个词本身更糟糕。人们用它来表示至少三种不同的东西。让我们也避免使用这个短语。 Git 文档使用术语remote-tracking branch 或remote-tracking branch name,它们是git branch -r 列出的名称。这句话还不错,但是里面的branch这个词是没有意义的。让我们称之为远程跟踪名称。
要学习的东西
在 Git 中重要的是 提交。了解这些关于提交的事情:
-
每一个都有一个唯一的哈希 ID。某个提交的哈希 ID 表示 that 提交。没有其他提交(在任何地方,在 any Git 存储库中)将具有 that 哈希 ID。 那个提交——在任何地方,在any Git 存储库中——将有那个哈希ID。
-
提交是在不同的 Git 克隆之间共享的内容。分支名称不共享。您的 Git 克隆具有 您的 分支名称,而其他一些克隆具有 其 分支名称。您可能想要使用相同的名称,以保持事情的正确性,但这取决于您(尽管 Git 会在这里提供帮助,因为这是很常见的事情)。
-
每个提交由两部分组成:
-
提交的主要数据是所有文件的快照。这些文件一直被冻结:它们以压缩、只读、仅 Git 和去重的形式存储。重复数据删除处理的事实是,大多数时候,大多数新提交大多包含与上一次提交相同的文件。存储在提交中的文件被冻结,甚至不能被非 Git 程序读取(更不用说写入)这一事实当然是个问题。
-
提交的另一部分是它的元数据。这包括提交人的姓名、他们的电子邮件地址以及他们提交提交的日期和时间戳何时。所有这些东西也是只读的。对于 Git 本身而言,至关重要的是,Git 会在此元数据中添加一些 previous 提交的哈希 ID。我们称这些为提交的父母。
提交表单链;分支名称帮助我们(和 Git)找到提交
鉴于我们有一些简单的提交序列:
... <-F <-G <-H
这里的每个字母代表一个实际的 Git 哈希 ID,我们最终得到一个线性的提交链,以提交 H 结束。如果我们知道H 的实际哈希ID,我们可以让Git 提取这个提交(见下文)。或者,我们可以让 Git 读取 H 的元数据并向我们显示提交的人......或者使用它来查找 H 的父提交 G 的实际哈希 ID。
由于G 和H 都持有快照,我们可以让Git 比较这两个快照。所有匹配的文件都是无趣的,因为它们匹配。任何 不 匹配的文件都更有趣,我们可以让 Git 找出其中的 不同 并向我们展示不同之处。这样,我们就可以看到我们改变了。 Git 不存储 更改:它只存储快照。但是我们可以看到快照随着的变化,因为提交有父级。
我们也可以让 Git 返回到 G 并使用它来查找 F,从而将 G 视为更改。从那里,我们可以回到F,并使用它来查找更早的提交,等等。但要做到这一切,我们需要链中 last 提交的实际哈希 ID。这就是分支名称的来源:像repo-branch1 这样的分支名称只是存储一些哈希ID。根据定义,存储在名称中的哈希 ID 是分支中的 last 提交。 Git 将从那里开始并向后工作。 在之后是否有后续提交也没关系:
...--E--F <-- br1
\
G--H <-- br2
这里H 是br2 中的last 提交(例如在F 和G 之后),而提交F 是最后在br1 中提交。通过F 向上提交在两个分支,但br1 开始或结束(取决于你如何看待它)在F 并向后工作,而br2 结束于H 并向后工作。
提取的提交
因为提交是只读的,我们实际上不能直接处理或使用它们。我们必须选择一些提交并将其设为当前提交。当我们这样做时,Git 提取所有与该提交相关的文件到一个工作区,Git 称之为 working tree 或 work-tree。这些是您可以查看和使用的文件。它们是普通的日常计算机文件,您计算机上的每个程序都可以使用。但它们实际上并不在 Git 中。
我们跑:
git checkout br2
(或 Git 2.23 或更高版本中的 git switch br2)。 Git 使用名称 br2 来查找该分支的最后一个(或 tip)提交(注意模棱两可的词,在这种情况下表示某些以 H 结尾的提交)。 Git 然后从该提交中提取文件,以便我们可以查看和使用它们,并将该提交设为当前提交,同时将该分支名称设为当前分支。我喜欢这样画:
...--E--F <-- br1
\
G--H <-- br2 (HEAD)
特殊名称HEAD附加到一个分支名称。这就是“在分支上”的含义:名称HEAD 定位到分支名称br2。分支名称本身定位提交,H,这是 Git 提取的那个。
如果我们进行 new 提交,Git 会打包一个快照,添加元数据,将新提交的 parent 设置为 current 提交H,并使用所有这些来写出新的提交。这为提交分配了新的、大而丑陋的随机(但实际上根本不是随机)哈希 ID,我将其称为 I。由于I 的父级是H,所以I 指向H。然后 Git 简单地将 I 的哈希 ID 写入当前 name,br2,给出:
...--E--F <-- br1
\
G--H--I <-- br2 (HEAD)
因此,分支名称的特殊之处在于它在我们进行新提交时会自动移动以指向新提交。 Git 通过将名称 HEAD 附加到分支名称来完成此操作。
Git 有其他名称(例如标记名称和远程跟踪名称)也 指向提交(通过存储提交哈希 ID),但您不能将 HEAD 附加到它们。
远程跟踪名称和git fetch
远程跟踪名称的形式类似于 origin/TLA1:它们以 远程名称 开头,例如 origin。远程名称是您使用git remote add时使用的名称; origin 只是第一个标准。如果您使用git clone 为您运行git init 和git remote add 等等,git clone 将使用origin 作为标准的第一个远程名称。 注意:您没有使用 git clone,因此当您运行 git remote add 时,名称将由您决定。
如上所述,您不能将HEAD 附加到远程跟踪名称。此外,您通常不会自己创建这些名称。你可以使用git branch -r 列出你现在拥有的那些,但是如果你不创建它们,你如何获得它们?
最后一个问题的答案是git fetch 命令创建了它们。 fetch 命令非常复杂(有好有坏),我肯定不会在这里详细介绍,但我们可以相对简单地描述它:git fetch 让你的 Git 调用其他 Git 并获取来自它的东西:
-
首先,您的 Git 列出了所有分支名称、标签名称和其他此类名称。这些带有哈希 ID — 主要是提交哈希 ID,尽管标签名称有时会稍微复杂一些。
-
然后您的 Git 会通过这些名称和哈希 ID 进行选择。您的 Git 可以判断您是否有提交,因为每个 Git 都使用 相同的随机外观但非随机哈希 ID 来处理相同的提交。因此,您的 Git 会立即知道您是否有 他们的 分支的提示提交。
如果你不这样做,你的 Git 会向他们的 Git 询问他们的提交。他们也提供提交的父母,你的 Git 会检查你是否有 那些 提交。通过这种拥有/想要的序列(通过一些重要的优化,避免每次都必须列出每个哈希 ID),你的 Git 可以确定他们有哪些提交,你没有,你需要,并要求他们.
-
他们打包所有这些提交并将它们发送给您。这里的细节可能会有很大差异,但在通常情况下,您会看到“计数”和“压缩”等,然后您的 Git 会收到一个包含提交和其他内部 Git 对象的包。然后,您的 Git 将所有内容保存在您的存储库中。
您现在拥有了之前的所有提交,加上任何您没有的提交(除非您的 Git 不想要它们,例如单分支克隆)。
-
最后,您的 Git 现在创建或更新您的远程跟踪名称。对于他们拥有的每个 branch 名称,您的 Git 会为您的存储库创建或更新相应的远程跟踪名称。
这意味着你永远不会直接获得他们的分支名称。您获取他们的分支名称并将它们更改 为您的远程跟踪名称。这充当您的 Git 的分支名称的记忆。它们由git fetch 创建或更新。在您运行git fetch 之前,您不会拥有origin/TLA1。
结论
重要的是提交。分支名称和其他名称可以帮助您(和 Git)find 提交。
您可以通过运行git fetch 获得远程跟踪名称。你告诉git fetch 呼叫哪个遥控器。您的 Git 会调用该遥控器并查看其分支并获取其提交,除非您已经拥有它们。然后您的 Git 会根据需要更新或创建远程跟踪名称。 (旁注:除非您告诉它,否则您的 Git 不会在此处删除“死”名称,因此一旦他们删除了某些分支名称,您将留下陈旧的远程跟踪名称。)
您可以随时创建自己的分支名称,但要创建名称,您必须有一个 commit 以使其指向。因此,您通常希望首先获得他们最新的:git fetch,然后第二个 Git 命令。
另外:git pull 表示运行 git fetch,然后运行第二个 Git 命令。由于需要这两个命令来做有用的事情,所以人们喜欢git pull,它运行这两个命令。我不喜欢git pull,因为我喜欢在这两个命令之间插入命令,并且可能使用git pull 为第二个命令提供的相对较薄的选项集以外的其他命令,但这取决于你。