Git Push:HEAD:refs / heads / <branch>和<branch>之间有什么区别?

时间:2016-07-21 03:48:58

标签: git push

命令1做什么命令2没有?

@foreach(var item in Model)

&#34; HEAD:refs / heads /&#34;?

的含义是什么?

3 个答案:

答案 0 :(得分:15)

VonC's answer是正确的(并且被赞成),但我认为另一种看待这种情况的方式可能更有意义。

请注意,所有这一切都假设您使用git push的四字形式,即git push remote refspec。这里的 remote 部分通常只是名称origin。我们稍后会更好地定义 refspec

git push做什么

git push需要做什么(因此确实如此)是在另一台机器上调用另一个Git实例, 1 然后给那个其他Git一组引用(通常是分支名称,有时是标记名称)要更新。 引用只是一个名称,例如masterv1.2,理想情况下应该是完全限定的(refs/heads/master或{{ 1}})这样我们就可以确定 kind 的引用是什么 - 分支,标记或其他。

为了让其他Git更新Git移交的引用,你的Git必须移交一些丑陋的SHA-1哈希:每个引用一个。换句话说,你的Git会要求他们的Git将他们的 refs/tags/v1.2设置为refs/heads/master。 (此时 - 实际上,在这一点之前,真的 - 你的Git和他们的Git谈论了你想要发送它们的对象,以及它们已经拥有的对象,所有这些都是由这些丑陋的哈希ID完成的。两个Gits同意将要发送的内容,您的ed4f38babf3d81693a68d06cd0f5872093c009f6counting objects然后向他们发送对象。现在,请设置一些名称&#34 ;部分几乎发生在最后。)

获取名称和哈希部分

请注意,Git的请求有两个部分:(1)完全限定的引用,以及(2)big-ugly-hash。 (事实上​​,还有第三部分,compressing objects标志,但这部分很简单,我们可以忽略它。)但是你的 Git在哪里得到这些?

如果你写:

--force

您已经发送 Git两条信息:名称git push origin somename ,您的Git用来查找网址,名称origin。你的Git使用它来计算全名。 somename是一个标签吗?如果是,则完整名称为somenamerefs/tags/somename是一个分支吗?如果是,则完整名称为somename。无论哪种方式都有效。当然,您也可以自己写出全名 - 如果名称是分支标记,您可能想要这样做,而不是让Git为你挑一个。 2

那么,你的Git在哪里获得了大丑陋的哈希?答案是:来自同一个名字。名称refs/heads/somename,无论是分支还是标记,只是命名某个特定的Git对象。如果你想自己看哈希,你可以随时做到:

somename

会告诉你。事实上,这是我如何得到git rev-parse somename :我去了Git的Git存储库并做了ed4f38babf3d81693a68d06cd0f5872093c009f6并打印出了这个哈希,因为git rev-parse v2.1.1是任何完整的有效标记自2.1.1版本发布以来Git存储库的副本。

请注意,当您执行时使用此表单 - 此v2.1.1表单 - Git会在您的<中查找 git push remote name 参数/ em>存储库用于两个目的:找出它的全名,并获取它的哈希值。 name所在的位置,只有该全名所指的内容并不重要。

但是Git不必使用你的分支机构(或标签)ID

HEAD的第四个参数称为 refspec ,其语法实际上允许用冒号分隔两个部分:

git push

在这种情况下, git push origin src:dst 部分提供名称,但dst部分提供哈希。 Git运行src部分到src并生成哈希。所以你可以:

git rev-parse

使用您的分支git push origin mybranch:refs/tags/v42 标识的任何提交哈希在其他Git存储库中创建标记v42

通常mybranch包含分支名称

在Git中,HEAD 总是命名当前提交。 通常它通过命名分支,并让分支命名提交来实现。因此,通常HEAD包含一个分支名称,如HEAD,分支名称始终会为您提供该分支的提示(这就是Git 的方式定义&#34; tip commit&#34 ;;参见the Git glossary中分支的定义。但是总是 3 master可以变成提交:

HEAD

因为$ git rev-parse HEAD 2b9288cc90175557766ef33e350e0514470b6ad4 是分支名称(然后是提示提交),或者你有一个&#34;分离的HEAD&#34;,在这种情况下,Git直接将当前提交ID存储在{ {1}}。

分离HEAD时推动

请记住,为了推送,Git需要获取这两个信息:哈希和(完整)名称。当HEAD 不是&#34; &#34;分离&#34;时,Git可以从中获取两者:HEAD有一个分支名称 - 以全名形式,实际上 - 分支名称具有哈希值。但当你在&#34;分离的HEAD&#34;模式,HEAD只有一个哈希。 Git 无法HEAD中找到分支名称。可能没有一个:您可能已经通过ID签出了提交,或者您可能已按标签名称签出,如:

HEAD

让你进入这个&#34;分离的HEAD&#34;模式。

在这种情况下,Git要求您提供源哈希HEAD - 您仍然可以使用名称$ git checkout v2.1.1 来获取它 - {{1}目的地名称。而且,如果您使用src作为来源,Git确实需要来拼出完整的目的地,因为Git现在无法告诉它是否应该是分支(HEAD)或标记(dst)。 4

其他形式的HEAD

您可以使用较少的参数运行refs/heads/dst,例如:

refs/tags/dst

甚至只是:

git push

这里发生的是,如果没有 git push ,Git会首先咨询您的git push origin 设置。通常这是git push (自Git 2.0版以来的默认值)。在这种情况下,Git只是使用refspec来确定要推送的内容 - 当然,只有当push.default没有分离时才能工作。这正是我们上面所描述的。

(其他三个设置也使用simple。其中一个 - 在Git 2.0版之前默认的那个 - 没有,但是这个特定的设置证明太容易出错,这就是为什么默认你可能不应该使用它,至少除非你是Git大师,否则你不应该使用它。)

(并且,如果你忽略了 HEAD ,Git会再次使用HEAD来确定推送到哪里,如果需要,默认为{{1} }。)

您还可以推送多个refspec:

HEAD

在这种情况下,每个refspec都以通常的方式处理:如果需要,获取其完全限定名称,这样你的Git每次都可以给他们的Git一个完全限定的名字;如果您没有使用remote表单(或者如果 使用HEAD表单,请查找其哈希ID,查找origin& #39;而不是ID。

您可以在refspecs中使用通配符:

git push origin branch1 branch2 tag1 HEAD:refs/tags/tag2

(有些贝壳会吃掉,fold, spindle, or mutilate src:dst,所以你可能需要使用引号,如本例所示;其他贝壳赢了 - 或者至少通常赢了# 39;但是引用它并没有什么坏处。这将推动所有您的分支,或者至少尝试。这往往过于热情,推动所有临时工作和实验分支,可能不是你想要的,但它是Git在2.0版之前默认做的。

而且,您可以使用空的src:dst

src

这是一种特殊情况的语法,意思是&#34;让我的Git让他们的Git 删除该引用&#34; (删除标签,拼出标签)。与分离的HEAD一样, 侧缺少完全限定名称意味着您应该完全限定他们的方面的名称。 (再次见脚注4)。

强制标志

如果在git push origin 'refs/heads/*:refs/heads/*' 命令中添加*,Git会将此标记传递给他们的Git。先生,请你将src设置为git push origin :refs/heads/deleteme 吗?&#34; - 你的Git会将其作为一个相当坚持的要求发送给你,而不是一个礼貌的请求 - &#34;他们的Git仍然可以拒绝任何一种方式,但默认情况下,即使它不合理,他们的Git也会这样做。

Refspecs允许您更紧密地控制此标志。单个refspec中的强制标志是前导加号--force。例如,假设您有git pushrefs/heads/master分支的新提交,以及ed4f38babf3d81693a68d06cd0f5872093c009f6的一组新的重新提交的提交,其他人都已同意你被允许强行推进。

你可以这样做:

+

但你可以将它们全部组合成一个大推力:

master

develop上的前导experiment使 一个命令(&#34;更新git push origin develop master; git push -f origin experiment !&#34;),同时将其他命令保留为礼貌请求(&#34;请先生,如果您愿意,请更新git push origin develop +experiment master +&#34;)。

(这对于experiment来说有点深奥,但实际上你每天都经常使用experiment,它使用带有develop标志的refspec来创建和更新你的远程 - 跟踪分支。)

1 如果&#34;其他回购&#34;在您的同一台计算机上,您使用基于master或本地路径的网址,这不是真的,但原则是相同的,操作也是一样的。

2 更好的是,首先不要让自己处于这种状况。让一个名称既是分支名称​​又是一个标记名称,这是非常令人困惑的。 (由于Git的缩写习惯,有类似的令人困惑的情况要避免:例如,不要使用类似于远程名称的名称来命名分支.Git会很好地处理它们,但可能没有。:-))

3 实际上,这条规则有一个例外,大多数人都不会注意到:当push命名一个&#34;未出生的分支时,#34;。大多数情况下,这发生在一个新的存储库中,它根本没有提交。显然,如果没有提交,则没有git fetch可以命名的提交ID。当您使用+创建新的孤儿分支时也会发生这种情况。

4 如果您使用不合格的名称,他们的Git会查找名称以使其符合资格。这意味着您可能不知道您尝试更新或删除的名称类型。无论如何,这通常不是一个好主意。

答案 1 :(得分:7)

正确的分支(意思不是detached HEAD)是存储在由HEAD直接引用的引用名称中的提交。
这意味着HEAD is a symbolic refrefname(其中包含实际提交),或直接提交(detached HEAD。另请参阅“HEAD: current commit”。

HEAD:refs/heads/<branch> refspec <src>:<dst>,其中<src>通常是您想要推送的分支的名称,但是它可以是任意“SHA-1表达式”,例如master~4HEAD<dst>告诉远程端哪个ref通过此推送更新。

如果您有以下状态:

   git checkout abranch

   --x--Y--W--Y--Z (HEAD abranch)
     |
  (origin/abranch)

git push origin aBranch将推动分支HEAD(此处为Z)或原点,从而产生;

   --x--Y--W--Y--Z (FEAD, abranch, origin/abranch)

但是如果你直接签出一个新的abranch提交:

git checkout abranch~2

         (HEAD)
           |
   --x--Y--W--Y--Z (abranch)
     |
  (origin/abranch)

然后使用第二种语法推送,您将仅将远程跟踪分支更新为W提交:

git push origin HEAD:refs/heads/abranch

         (HEAD)
           |
   --x--Y--W--Y--Z (abranch)
           |
  (origin/abranch)
尽管HEAD没有引用git push origin aBranch

abranch仍会推动完整的分支。

答案 2 :(得分:1)

第一个命令显式推送具有给定名称的分支。例如,如果你有一个与你的某个分支名称相同的标记,那么第二个命令就不明确了。