我写过其他关于跟踪的问题,但我认为我解释得很差。这是一个快速示例,以显示我的问题:
- 我创建了一个 Gitlab 项目。
- 我用
git clone
在我的计算机上克隆了这个项目。 - 我在 Gitlab Web 界面中创建了一个新分支(带有"+"按钮):
my_server_branch
. - 我将一个文件放入此分支(也带有 Web 界面)。
所以,在这一步,我的电脑对my_server_branch
一无所知。
我已经阅读了我应该在我的计算机上创建一个"跟踪分支":
git branch --track my_server_branch origin/my_server_branch
我没有运行此命令。我只是简单地输入(在我的主分支上,在我的计算机上):
git pull
这是我在控制台中看到的内容:
* [new branch] my_server_branch -> origin/my_server_branch
所以当我启动拉取时,git 会检测到有一个新的分支。所以,再一次,我的问题是:如果 gits 检测到并执行这些操作,跟踪分支的优势是什么?
跟踪肯定有一个优势,因为如果没有,这个命令将不存在......但我没有看到这种优势。
如果我键入git branch --track
命令,这就是我得到的:
error: the requested upstream branch 'origin/my_server_branch' does not exist
这里有一堆概念,你需要在脑海中分开;然后还有一个术语问题。
这些是概念:
-
分支名称如
master
;
远程跟踪 - 名称(通常称为远程跟踪分支),如
origin/master
; - 上游的想法;和
- 提交,由其哈希 ID标识;保存文件和一些元数据;并通过该元数据形成链。
前两个 - 分支名称和远程跟踪名称 - 非常密切相关,并且与v2.1
等标签名称一起,都归入一个Git称为引用的概念。
术语问题是,有时,一些分支 - 一些名称,如master
- 据说正在跟踪。 像origin/master
这样的其他名称被称为远程跟踪分支,看起来是一回事。 不是! 这就是为什么我将后者称为远程跟踪名称,以避免使用分支一词,以及为什么我建议你考虑像master
这样的分支名称而不是动词跟踪,要么有上游,要么没有上游。 这回避了棘手的单词轨道(当应用于工作树中的文件时,它还有另一种含义)。
让我们继续您的操作:
这是一个快速示例,以显示我的问题:
我创建了一个 gitlab 项目
我已经用
git clone
命令在我的计算机上克隆了这个项目
此时,您有两个单独的存储库。 GitLab 服务器计算机上有一个,您自己的计算机上有一个。 我们将 GitLab 称为"他们的",将您计算机上的称为"您的",尽管从某种意义上说,它们都是您的。 您的存储库与他们的文件库非常相似,但不完全相同:它是一个克隆,它有办法将其标识为副本而不是原始副本。
稍后我们将回到您的计算机上的存储库。 不过,接下来的几个步骤都发生在他们的存储库中。
- 然后,我在 gitlab Web 界面中创建了一个新分支(带有"+"按钮):
my_server_branch
好的,所以在这一点上,他们的Git 存储库有一个你不知道的分支。
- 我已经将一个文件放入此分支(也带有 gitlab Web 界面)
从技术上讲,您不能像这样将文件放入存储库中。 您所做的是添加一个新提交,新提交包含该文件。
这很重要,因为分支名称的工作方式是记住分支中最后一个提交的哈希 ID。 添加新提交时,存储在分支名称中的哈希 ID 会更改以记住新提交。 新提交会记住上一次提交。
如果我们绘制这些,使用单个大写字母来代替实际的提交哈希 ID,我们会得到一张像这样的图片,用于一个简单的三提交存储库,只有一个master
分支:
A <-B <-C <-- master
在这里,名称master
记住提交C
的实际哈希 ID。 该提交本身会记住提交B
的实际哈希ID,从而记住提交A
的哈希ID。 所以 Git 只需要有名字master
记住提交C
的 ID:其余的,它通过查看提交本身来找到。
(我们说master
指向C
,C
指向B
,B
指向A
。 由于A
是存储库中有史以来的第一次提交,因此它无处可去:这就是告诉我们和 Git 可以停下来休息的原因。 没有以前的病史可供检查。 提交是历史记录,历史记录变为C 然后 B 然后 A。
要将新分支添加到存储库,我们通常会在存储库中选择一个现有的提交并将其签出,以便它是当前的提交,然后添加一个指向同一提交的新名称:
A--B--C <-- master, my_server_branch (HEAD)
当我们进行新提交时,Git 需要知道要更新哪个名称,因此 Git 将特殊名称HEAD
(像这样全部大写)附加到分支名称。 如果我们使用的是本地计算机(而不是 Web 界面),我们将创建一个文件,使用git add
添加它,然后运行git commit
进行新的提交。 如果我们使用的是Web界面,GitLab在他们的存储库中几乎做同样的事情,它只是隐藏在他们的Web界面后面。 他们最终得到:
A--B--C <-- master
D <-- my_server_branch (HEAD)
尽管他们可能会这样做,以至于无论如何他们都会HEAD
依附于master
。 例如,GitHub就是这样做的,而无需移动HEAD
。 无论如何,既然这是他们的HEAD
,而不是你的,所以现在并不是那么重要。
克隆获得自己的分支
现在是时候回顾一下你自己的仓库了。 当您运行时:
git clone <url>
你让您的计算机创建了一个新的、空的 Git 存储库,没有提交,没有分支,基本上只有空的存储库 shell。 然后,你让计算机上的 Git 通过从他们的Git 中获取所有提交来填充该 shell。 因此,如果他们有三个简单的提交:
A--B--C <-- master
你的 Git 得到了这三个提交:
A--B--C
(内部的向后箭头太烦人了,无法绘制,但它们仍然存在:C
指向B
,B
指向A
)。
哈希ID都匹配:宇宙中的每个Git都会同意提交C
,这使得它的哈希IDC
的哈希ID。 因此,您的 Git 和他们的 Git 可以通过查看这些哈希 ID 来判断哪个 Git 具有哪些提交。 但是你的Git 仍然没有任何分支。
你的 Git 问他们的 Git 他们所有的分支和标签名称是什么,他们说:我的master
保存提交C
的哈希 ID。所以你的 Git 现在创建,不是master
,而是origin/master
,指向提交C
:
A--B--C <-- origin/master
没有提交,也没有分支,所以你的 Git 完成了复制。 你的 Git 现在执行git clone
的最后一步,即运行:
git checkout master
你可以让你的 Git 使用其他名称,如果你不这样做,你的 Git 会问他们的Git 使用哪个名称;但这是常见的情况:你的 Git 试图检查你的master
。
你没有master
.然而,无论如何,此结帐还是成功了。 它成功的原因是他们的Git 有一个master
,你的 Git 将其复制到你的origin/master
.所以你的 Git 不仅仅是结账失败,而是对自己说:嗯,没有master
,但有origin/master
......这看起来很像master
,我敢打赌你的意思是我应该使用origin/master
制作master
。所以你的 Git 就是这样做的:
git checkout --track master origin/master
创建自己的master
,并将其上游设置为origin/master
。 所以现在你有这个:
A--B--C <-- master (HEAD), origin/master
您的master
分支现在存在,并且origin/master
作为其上游。
侧边栏:如果您感到困惑,请放心,这是令人困惑的!
令人困惑的说法是,您的master
分支 (1) 现在正在跟踪 (2) 您的远程跟踪分支 (3, 4, 5)origin/master
从远程 (6)origin
。 在这里,在第 (1) 和 (5) 点,两个单词或短语都使用单词分支,但两者都有不同的含义。 在(2)和(4)处,我们有跟踪这个词,两者都意味着不同的东西。 在(3)和(6)中,我们有"远程"一词,两者都意味着不同的东西。 你可以看到为什么我不喜欢这些词,更喜欢用上游origin/master
来称它为分支名称master
,origin/master
是与远程origin
相关联的远程跟踪名称。 我仍然必须使用两次远程这个词,但">远程跟踪"至少是连字符。
一种正确和好的方法,可以在当地获得my_server_branch
在他们的Git 中进行了my_server_branch
,并在那里添加了提交D
,您现在可以做的是运行以下命令:
git fetch
在计算机上您自己的 Git 中。 (如果要明确,可以使用git fetch origin
。 这让你的 Git 调用他们的 Git 并再次询问它的分支名称列表。 这次他们说:我有master
,在提交C
。 我有my_server_branch
,在提交D
。你的 Git 说:啊,我已经提交了C
,所以没有问题。 不过,给我承诺D
。他们这样做了,现在你的 Git 和他们的 Git 之间的对话已经完成。 现在,您的 Git 会更新您的origin/master
以指向C
- 这根本没有变化 - 并创建您的origin/my_server_branch
,指向新的提交D
。 所以现在你有:
A--B--C <-- master (HEAD), origin/master
D <-- origin/my_server_branch
您现在可以运行git checkout my_server_branch
. 和以前一样,你目前没有my_server_branch
,但不仅仅是失败,你的 Git 会说:啊哈,我没有my_server_branch
。 但我确实有origin/my_server_branch
. 我将创建my_server_branch
,指向提交D
。 我将my_server_branch
的上游设置为origin/my_server_branch
。 然后我会做你要求的结账。结果是:
A--B--C <-- master, origin/master
D <-- my_server_branch (HEAD), origin/my_server_branch
您几乎不需要使用git checkout --track
你唯一需要git checkout --track
的时候是git checkout
不会为你做正确的事情的时候。 这发生在两种情况下:
假设您有多个遥控器,例如,如果您有
origin
,再加上第二个遥控器fred
,用于从 Fred 的存储库中获取内容。 进一步假设您从原产地hello
分支复制了自己的origin/hello
,并且弗雷德有弗雷德的hello
,现在已复制到您的fred/hello
。 如果你尝试git checkout hello
,你的 Git 会找到两个候选者——fred/hello
和origin/hello
——并且不知道该使用哪一个。 所以现在你可以改为运行:git checkout --track fred/hello
如果你真的想使用弗雷德的,或者:
git checkout --track origin/hello
如果你真的想使用Origin的。
或者,如果出于某种奇怪的原因,您有,比如说,
origin/my_server_branch
,但在您的存储库中,您想将其称为bob_server_branch
。 使用git checkout my_server_branch
可以让你my_server_branch
;当然,使用git checkout bob_server_branch
尝试找到origin/bob_server_branch
. 所以在这里,你需要长格式:git checkout --track bob_server_branch origin/my_server_branch
关于git pull
git pull
命令是以下命令的简写:
- 运行
git fetch
; 然后,前提是成功 - 运行第二个 Git 命令,通常
git merge
。
由于git fetch
将(无论如何,当使用正确的选项运行时)从origin
的分支创建和/或更新您的origin/*
远程跟踪名称,因此git pull
的前半部分为您origin/my_server_branch
。
第二个命令 -git merge
,或者,如果你告诉它改用git rebase
,git rebase
- 接受第一个命令引入的提交,并使用这些提交进行合并或变基。
我不喜欢git pull
命令,原因有很多,其中一些纯粹是历史的(git pull
偶尔会在一些罕见但并非闻所未闻的情况下破坏你的本地工作,我至少发生过一次)。 不过,最实际的反对意见非常简单:在你看到git fetch
获取的内容之前,你怎么知道你是要运行git merge
、git rebase
,还是完全运行其他东西?所以我宁愿避免git pull
:我先运行git fetch
,然后可能运行git merge
或git rebase
,或者完全做其他事情。 该怎么做取决于我从git fetch
中看到的内容(当然,还有我在这个特定存储库上做什么)。
有一些例外,特别是对于我使用只读的存储库 - 我只想要他们的最新提交及其历史记录,所以git pull
可能没问题,只要它们表现良好 - 或者我控制两端的地方,例如,origin
存储库在GitHub上确实是我的,我知道我在那里放了什么。 但即使对于后一种情况,我也倾向于避免git pull
,因为有时我会忘记我放入哪个存储库的内容。 使用git fetch
让我先检查一下。
我不是 100% 确定我理解您在问什么,但跟踪远程分支的优势在于您可以推送和拉取它。
在本地查看分支机构:git checkout my_server_branch
进行更改并提交。
现在git push
会将您的更改推送到远程origin/my_server_branch
分支。
git pull
运行git fetch
和git merge
。很高兴知道
使用此命令的第一部分(git fetch
),您从远程获取所有分支。正如您在此处阅读的,此命令获取所有refs
。
默认情况下,当您从远程签出新分支时,git 会将其创建为新的上游分支。基本上,当您获取所有分支时,您询问的命令是为您隐式运行的。