需要了解分支跟踪

时间:2019-05-02 19:54:00

标签: git

我还写了其他有关跟踪的问题,但我认为我对我不了解的内容的解释不充分。这是一个简单的示例,以显示我的问题:

  • 我创建了一个Gitlab项目。
  • 我使用git clone在计算机上克隆了此项目。
  • 我在Gitlab Web界面(使用“ +”按钮)中创建了一个新分支:my_server_branch
  • 我将单个文件放入此分支(也通过Web界面)。

因此,在这一步中,我的计算机对my_server_branch一无所知。

我已阅读,我应该在计算机上使用以下命令创建一个“跟踪分支”:

git branch --track my_server_branch origin/my_server_branch

我还没有运行此命令。我只需键入(在我的计算机的master分支上):

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

3 个答案:

答案 0 :(得分:2)

这里有很多概念,您需要分开思考。然后还有一个术语问题。

这些是概念:

  • 分支名称,例如master;
  • 远程跟踪名称(通常称为远程跟踪分支),例如origin/master;
  • 上游的概念; 和
  • commits ,由其哈希ID标识; 包含文件以及一些元数据; ,并通过该元数据形成链。 / li>

前两个名称(分支名称和远程跟踪名称)密切相关,并且与诸如v2.1之类的标记名称一起被归为一个概念,Git称之为 reference

术语上的问题是,有时某些分支(例如master之类的名称)被称为 tracking 。诸如origin/master之类的其他名称称为远程跟踪分支,看起来好像是同一回事。不是!这就是为什么我将后者称为远程跟踪名称以避免使用 branch 这个词的原因,并且为什么我建议您代替动词 tracking 将诸如master之类的分支名称考虑为具有上游不具有上游。这回避了棘手的单词 track (当应用于工作树中的文件时,它还有另一个含义)。

让我们继续执行您的操作:

  

这里是一个简单的例子,以显示我的问题:

     
      
  • 我已经创建了一个gitlab项目

  •   
  • 我已使用git clone命令在计算机上克隆了该项目

  •   

此时,您有两个单独的存储库。 GitLab服务器计算机上有一台计算机,而您自己的计算机上有一台计算机。我们将GitLab称为“他们的”,而将计算机上的一个称为“您的”,尽管从某种意义上说,它们都是您的。您的存储库与它们的存储库非常相似,但不完全相同:它是一个克隆,并且可以将其标识为副本而不是原始副本。

稍后,我们将回到您计算机上的存储库。接下来的几个步骤都发生在存储库中。

  
      
  • 然后,我在gitlab Web界面中创建了一个新分支(使用'+'按钮):my_server_branch
  •   

好,因此,此时, Git存储库中有一个您不知道的分支。

  
      
  • 我已经将单个文件放入此分支(也使用gitlab Web界面)
  •   

从技术上讲,您不能将文件放入这样的存储库中。您要做的是添加一个新的 commit ,其中新的提交包含文件。

这很重要,因为分支名称的工作方式是记住分支中 last 提交的哈希ID。添加新提交时,存储在分支名称中的哈希ID会更改以记住新提交。新提交会记住先前的最后提交。

如果我们用单个大写字母代表实际的提交哈希ID来绘制它们,我们将得到一个类似这样的图片,它是一个简单的三提交存储库,它只有一个master分支:

A <-B <-C   <-- master

在这里,名称 master会记住提交C的实际哈希ID。该提交本身会记住提交B的实际哈希ID,而它会记住提交A的哈希ID。因此,Git仅需要具有名称master记住提交C的ID:其余的则是通过查看提交本身来找到的。

(我们说master 指向 CC指向BB指向{{ 1}}。由于A是存储库中有史以来的第一个提交,因此它毫无意义:这就是告诉我们和Git我们可以停下来休息的地方。没有以前的历史可以检查。 em>是历史记录,历史记录依次为 C,B,A 。)

要向存储库添加新分支,我们通常会在存储库中选择一些现有提交并检出它,使其成为当前提交,然后添加指向同一提交的新名称:

A

Git在进行新提交时需要知道哪个名称进行更新,因此Git会将特殊名称A--B--C <-- master, my_server_branch (HEAD) (大写为此类)附加到分支名称。如果使用的是本地计算机(而不是Web界面),则将创建一个文件,使用HEAD进行添加,然后运行git add进行新的提交。如果我们使用的是Web界面,则GitLab在存储库中几乎会做同样的事情,它只是隐藏在其Web界面后面。他们最终以:

git commit

尽管他们可能会以这样的方式进行操作:无论如何他们仍然将A--B--C <-- master \ D <-- my_server_branch (HEAD) 附加到HEAD上。例如,这就是GitHub无需移动master的方式。无论如何,由于它不是他们的{em> {em> HEAD,所以现在并不是那么重要。

克隆有自己的分支

现在是时候回顾您自己的存储库了。当您运行时:

HEAD

您让计算机创建了一个新的空Git存储库,其中包含 no 提交, no 分支,除了空的存储库外壳,基本上什么也没有。然后,通过从他们的 Git中抓取 all 提交,将Git放入计算机中。因此,如果他们有三个简单的提交:

git clone <url>

您的Git获得了这三个提交:

A--B--C   <-- master

(内部向后指向的箭头太烦人了,无法绘制,但仍然存在:A--B--C 指向CB指向B

所有哈希ID都匹配:宇宙中的每个Git都会同意提交A中的内容,这将使其哈希ID C成为哈希ID。因此,您的Git及其Git只需查看这些哈希ID即可知道哪个Git具有哪些提交。但是您的 Git仍然没有任何分支。

您的Git询问他们的Git他们所有的分支和标记名称是什么,他们说:我的C拥有提交master的哈希ID。现在,您的Git创建而不是C,而是创建master,指向提交origin/master

C

没有提交,也没有分支,因此您的Git复制完成。您的Git现在执行A--B--C <-- origin/master 的最后一步,即运行:

git clone

您可以让Git使用其他名称,如果不使用,您的Git会询问他们的 Git使用哪个名称;但这是通常的常见情况:您的Git尝试检出您的git checkout master

您没有master 但是,此结帐还是成功的。它成功的原因是他们的 Git有一个master,而您的 Git将其复制到了您的 master。因此,您的Git不仅没有通过结账,还对自己说:嗯,没有origin/master,但是有master ...看起来很像origin/master,我打赌您的意思是我应该使用master make master因此,您的Git会这样做:

origin/master
创建您自己的git checkout --track master origin/master ,并将其上游设置为master。所以现在你有了这个:

origin/master

您的A--B--C <-- master (HEAD), origin/master 分支现在存在,并且以master作为上游分支。

侧边栏:如果您感到困惑,放心,这很令人困惑!

令人困惑的方式是,您的origin/master分支(1)现在正在从远程(6)跟踪(2)您的远程跟踪分支(3,4,5)master { {1}}。这里,在第(1)和(5)点,两个单词或短语都使用单词 branch ,但两者的含义有所不同。在(2)和(4),我们有 tracking 字样,两者的含义有所不同。在(3)和(6),我们有一个单词 remote ,它们的含义有所不同。您可以看到为什么我不喜欢这些单词,并且更喜欢将其称为分支名称 origin/master,将其与上游 origin一起使用, master是与远程 origin/master相关联的远程跟踪名称。我仍然必须两次使用 remote 这个词,但是“ remote-tracking”至少是连字符的。

在本地获取origin/master的一种正确和好方法

在其 Git中创建了origin,并在其中添加了提交my_server_branch,现在可以执行以下命令:

my_server_branch

在计算机上使用自己的Git。 (如果要明确,可以使用D。)这使您的Git调用其Git并再次询问它的分支名称列表。这次他们说:我有git fetch ,正在提交git fetch origin。我有master,正在提交C您的Git说:啊,我已经有了my_server_branch了,所以没问题。不过,还是给我D他们这样做了,现在您的Git和他们的Git之间的对话完成了。现在,您的Git将您的C更新为指向D,这根本没有改变,然后创建您的origin/master,指向新的提交C 。所以现在您有了:

origin/my_server_branch

您现在可以运行D。和以前一样,您目前没有A--B--C <-- master (HEAD), origin/master \ D <-- origin/my_server_branch ,但是您的Git不会失败,而是会说:我还没有{{1} }。但是我确实有git checkout my_server_branch。我将创建 my_server_branch,指向提交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,可以从Fred的存储库中获取内容。进一步假设您在分支机构git checkout上复制了自己的origin,并且Fred将 Fred的 fred复制到了{{1} }。如果您尝试origin/hello,您的Git将会找到两个候选对象-hellohello-并且不知道使用哪个候选对象。因此,您现在可以改为运行:

    fred/hello

    如果您真的想使用Fred的话,或者:

    git checkout hello

    如果您真的想使用原产地。

  • 或者,如果由于某些奇怪的原因,您拥有fred/hello,但是在您的存储库中,您希望将其称为origin/hello。使用git checkout --track fred/hello 将使您git checkout --track origin/hello ;当然,使用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命令,通常是git checkout --track bob_server_branch origin/my_server_branch

由于git pull会(当使用正确的选项运行时)会创建和/或更新来自git pull的{​​{1}}远程跟踪名称分支,是git fetch的前半部分为您创造了git merge

第二个命令git fetch,或者,如果您告诉它使用origin/*,则origin –接受第一个命令带来的提交命令并使用它们进行合并或变基。

由于许多原因,我不喜欢git pull命令,其中一些纯粹是历史原因(origin/my_server_branch有时会以一些罕见但并非鲜为人知的方式销毁您的本地作品案例,而我至少经历过一次)。不过,最实际的反对意见非常简单:在您看到git merge所获取的内容之前,如何知道您是否要运行git rebasegit rebase或其他所有内容?< / em>因此,我宁愿避免使用git pull:首先运行git pull,然后 可以运行git fetchgit merge,或者执行某些操作其他完全。该做什么取决于我从git rebase看到的内容(当然,还有我正在使用此特定存储库的内容)。

偶尔会有一些例外,尤其是对于我使用只读存储库的情况而言-我只想要它们的最新提交及其历史记录,因此git pull可能 很好,只要因为它们表现良好-或在我控制两端的地方,例如git fetch存储库确实位于GitHub上,我知道我在其中放置的内容。但是,即使是后一种情况,我也倾向于避免使用git merge,因为有时我会忘记放入哪个存储库中的内容。我先使用git rebase进行检查。

答案 1 :(得分:1)

我不确定是否100%理解您的要求,但是跟踪远程分支的好处是您可以推入和拉出它。

在本地签出分支: git checkout my_server_branch

进行更改并提交。

现在git push会将您的更改推送到远程origin/my_server_branch分支。

答案 2 :(得分:1)

git pull运行git fetchgit merge。很高兴知道

使用此命令的第一部分(git fetch),您从远程获取了所有分支。如您所读here,此命令将提取所有refs

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