需要了解分支跟踪

  • 本文关键字:跟踪 分支 了解 git
  • 更新时间 :
  • 英文 :


我写过其他关于跟踪的问题,但我认为我解释得很差。这是一个快速示例,以显示我的问题:

  • 我创建了一个 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指向CC指向BB指向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指向BB指向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来称它为分支名称masterorigin/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/helloorigin/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 rebasegit rebase- 接受第一个命令引入的提交,并使用这些提交进行合并或变基。

我不喜欢git pull命令,原因有很多,其中一些纯粹是历史的(git pull偶尔会在一些罕见但并非闻所未闻的情况下破坏你的本地工作,我至少发生过一次)。 不过,最实际的反对意见非常简单:在你看到git fetch获取的内容之前,你怎么知道你是要运行git mergegit rebase,还是完全运行其他东西?所以我宁愿避免git pull:我先运行git fetch,然后可能运行git mergegit 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 fetchgit merge。很高兴知道

使用此命令的第一部分(git fetch),您从远程获取所有分支。正如您在此处阅读的,此命令获取所有refs

默认情况下,当您从远程签出新分支时,git 会将其创建为新的上游分支。基本上,当您获取所有分支时,您询问的命令是为您隐式运行的。

最新更新