为什么Git使用令人困惑的相似名称,例如“ origin foo”,“ remotes / origin / foo”和“ origin / foo”?

时间:2019-08-01 15:38:20

标签: git git-fork

我被要求使用Fork工作流程,即我必须处理多个具有相同或相似名称的分支。为什么要使用这些不同的版本?

以下是一些不同命名约定的示例:

  • mybranch
  • 起源mybranch
  • 起源/ mybranch
  • 远程处理/来源/ mybranch
  • 上游mybranch
  • 上游/ mybranch

2 个答案:

答案 0 :(得分:4)

请谨慎背诵一系列记忆的Git命令。 Git有一个simple but powerful object model。了解git命令在对象模型方面的工作方式,以及您想做什么适合命令的工作。以我的经验,运行记忆的命令有时会导致多次重复调用相同的顺序,这会以奇怪的方式纠缠历史。困惑的队友求助后,我坐下来,看一下当前状态,挠着头,问:“你怎么进入这种状态?”

用于跟踪分支(例如origin/mybranchremotes/origin/mybranch甚至是refs/remotes/origin/mybranch以及upstream/mybranch的命名方案)的实现泄漏了分支名称空间 viz 的详细信息。refsremotes,origin是解压缩存储时的物理目录。请参阅git pack-refs documentation或在.git/refs下面的目录层次结构中摸索。

为方便用户,Git接受分支的缩写名称,类似于用户无需键入整个40个字符的SHA1对象名称的方式。 git rev-parse documentation解释。

  

<refname>,例如masterheads/masterrefs/heads/master
  符号引用名称。例如。 master通常表示提交对象   由refs/heads/master引用。如果您碰巧兼有   heads/mastertags/master,您可以明确地说出heads/master来告诉Git您的意思。如果模棱两可,则<refname>会通过遵循以下规则中的第一个匹配项来消除歧义:

     
      
  1. 如果存在$GIT_DIR/<refname>,这就是您的意思(这通常仅对HEADFETCH_HEADORIG_HEADMERGE_HEADCHERRY_PICK_HEAD);

  2.   
  3. 否则,如果存在refs/<refname>,则为refs/tags/<refname>

  4.   
  5. 否则,如果存在refs/heads/<refname>,则为refs/remotes/<refname>

  6.   
  7. 否则,如果存在refs/remotes/<refname>/HEAD,则为refs/remotes/origin/foo

  8.   
  9. 否则,如果存在mybranch,则为mybranch

  10.   
  11. 否则,如果存在origin/mybranch

  12.   

Git文档将upstream/mybranch称为符号全名完整refname ,它们对于按名称指定精确引用非常有用。含糊不清-例如在自动化程序中。

请注意,mybranch指的是您的 local origin/mybranch,而不是其跟踪分支(或远程跟踪分支)origin mybranch或{{1} }。每次获取或拉取都会更新跟踪分支。将它们视为您历史记录中的书签或沿您上次拉线时遥控器所在分支的路径上的路标。 origin/mybranchmybranch可能引用不同的提交,而且这种情况经常发生。

相反,/从不引用git push origin master git push <remote> <branch> 。 Git没有“ <branch>运算符”。这种想法使这两个两个独立的论点脱离了上下文。请记住,Git是一套命令行工具,命令行工具接受参数作为位置参数,而Unix命令外壳通过空格将参数拆分。在上下文中,命令

master

具有一般形式

origin

也就是说,其中一个参数命名为远程,另一个参数命名为(通常是本地)分支。因此,用英语来说,这意味着“将我的// Change rel “nofollow” to rel “dofollow” on Shop Page Product button add_filter( 'woocommerce_loop_add_to_cart_link', 'add_to_cart_dofollow', 10, 2 ); function add_to_cart_dofollow($html, $product){ if($product->product_type == 'simple') { $html = sprintf( '<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" aria-label="Добавить в корзину" data-product_sku="%s" class="%s" %s>%s</a>', esc_url( $product->add_to_cart_url() ), esc_attr( isset( $quantity ) ? $quantity : 1 ), esc_attr( $product->get_id() ), esc_attr( $product->get_sku() ), esc_attr( isset( $args['class'] ) ? $args['class'] : 'button product_type_variable add_to_cart_button' ), esc_attr( isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : ''), esc_html( $product->add_to_cart_text() ) ); }else { $html = sprintf( '<a href="%s" data-quantity="%s" data-product_id="%s" aria-label="Выбрать" data-product_sku="%s" class="%s" %s>%s</a>', esc_url( $product->add_to_cart_url() ), esc_attr( isset( $quantity ) ? $quantity : 1 ), esc_attr( $product->get_id() ), esc_attr( $product->get_sku() ), esc_attr( isset( $args['class'] ) ? $args['class'] : 'button product_type_variable add_to_cart_button' ), esc_attr( isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : ''), esc_html( $product->add_to_cart_text() ) ); } return $html; } (在这种情况下命名为\hfill上的提交推送到远程(在这种情况下命名为\vfill)上。”

答案 1 :(得分:3)

这些东西之所以令人困惑,是因为Git并非一次写完。 Git写了很多年了(现在已经接近了二十年)。早期犯了一些错误;有些事情不是很方便;并且Git可以保持向后兼容,如果不早的话可以追溯到2005年。

您需要知道的是,今天的Git具有:

  • 远程远程是一个简单的简称,例如originupstream。遥控器具有几个功能。最重要的是,它使您避免一遍又一遍地重新输入URL。

    运行git clone时,将自动获得一个遥控器。该遥控器的名称默认为origin(您可以更改它,但是没有真正的理由这样做)。因此,几乎每个Git存储库都有一个origin,因为大多数人都是通过克隆来创建Git存储库的。

  • “ Fork”只是“克隆”的网络托管服务变体,具有附加功能。在虚拟主机服务上创建分支之后,通常必须创建自己的附加克隆。因此,在创建叉子之后,现在有三个个单独的Git存储库:

    • 您在网络托管服务上分叉的那个。您一点都不控制。
    • 您的虚拟主机服务分支。托管服务实际上控制了该Git存储库,但是它们提供了网页和URL,以将控制权委派给您。
    • 您在自己的计算机上的克隆。此克隆具有一个URL,名称为origin,可用于控制Web托管服务上的fork。


    为了处理所有三个存储库,您将在克隆中创建这些“远程”对象的另一个。您已经有了一个名为origin的名称,因此您创建了第二个名称为upstream的名称,该名称在网络托管服务中包含 other 的URL。这对您是只读的-您将只用它从原始存储库(您分叉的存储库)中中获取东西。

    < / li>
  • 远程跟踪名称。 Git称这些为远程跟踪分支名称,但我认为在其中加上 branch 一词只是令人困惑。说远程跟踪分支而没有单词 name ,这太诱人了,然后它变得真的令人困惑,因此请忽略单词 branch 完全相同,只需将它们称为远程跟踪名称

一点点历史

原始Git(可能由于Linux和/或Linus而开始流行)根本没有远程远程跟踪名称。人们只是一遍又一遍地输入网址。您将运行:

git pull <url> <branch>

一切都非常明显和显而易见:您正在告诉自己的Git:嘿Git,去用我刚给您的URL调用另一个Git,然后从我在命令中在此处命名的分支中获取提交。行。

充满了错误和错别字的机会。特别是,谁想一遍又一遍地输入http://some.web.host/some/long/path/to/repo.git?因此,进行了许多尝试,为您提供一种简短且易于输入的方式,使您不必每次都重新输入URL。流行的是远程

git remote add origin http://some.web.host/some/long/path/to/repo.git

现在,您可以输入origin而不是长URL。此外,由于您可能一次输入URL即可进行git clonegit clone 本身可以为您创建origin 。不会再有错别字或错误了。显然,这更好。

同时...为什么还要输入分支名称?假设您一遍又一遍地执行此操作:

git checkout dev
...
git pull origin dev
...
git push origin dev
...
git pull origin dev
...
git push origin dev

不必每次都输入origin dev会很好吗?好吧,这也添加到了Git中。现在,Git可以为每个分支记住一个上游 此上游与您的upstream遥控器不同。 这是您的错:-),尽管毫无疑问,您只是遵循了一些教程示例。这些教程全都称为第二个远程“上游”,而没有考虑分支也有一个上游的事实,因此现在传统上在 second中使用文字字符串upstream 因为有分叉而需要的遥控器。

与此同时,叉子甚至都不是一回事。它们由于GitHub和Bitbucket和其他网络托管服务而变得流行。 Fork仍然不是 Git 的东西:这是Web托管服务提供的东西。就Git而言,所有Git存储库都只是Git存储库。不必担心某个东西是否是“叉子”,它只是一个Git存储库。

鉴于您有一个名为origin远程,并且这是一个Git存储库,如果您的 Git存储库可以调用它们,找到它可能会很好列出所有分支(以及相应的提交),并为您记住它们。您的Git 这样做。这就是这些远程跟踪名称的全部内容。

假设您有一个名为dev的分支,并将其发送到origin并始终从origin获取。另一个Git,位于Web托管服务上,其URL保留在您的名字origin下,调用分支dev。每当您的Git调用他们的Git时,您的Git就会看到他们的 dev。您的Git将其dev复制到您的远程跟踪名称,即origin/dev。您的远程跟踪名称是Git记住的方式:这是他们的分支,这是我上次与他们交谈的时间。

最后,分支的上游通常将是您正在使用的远程跟踪名称。也就是说,我们刚刚注意到您一直将提交提交到origin的{​​{1}},并将提交从dev的{​​{1}}提交到{{1} }。因此,您将告诉自己的Git:将我的origin的上游设置为我的dev整洁但令人困惑的将您的dev绑定到两者dev 及其 origin/dev(您记得为dev)。

但是origin必须与2005或更早版本兼容。那时,没有 dev;没有origin/dev没有;您必须输入:

git pull

使用:

origin

向后兼容2005-or-before:Git只是将origin/dev转换为URL。您在这里使用git pull <big-long-url> dev 而不是git pull origin dev 是因为您要提供两个单独的内容:它们的Git的URL和它们的Git使用的分支名称。

每当您在大多数情况下在Git中进行本地工作时,都使用origin:您的Git会记住他们的Git origin dev。但是,当您使用origin/dev时,您不在本地工作:您的Git通过URL通过Internet拨打另一个Git。因此,在这里您可以使用单独的分支名称。