遥控器/原点/HEAD应设置为什么? [英] What should remotes/origin/HEAD set to?

查看:179
本文介绍了遥控器/原点/HEAD应设置为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果开发人员正在处理develop分支,则用于新项目

1)克隆

git clone <git_url>应该能够在不使用-b选项的情况下自动在本地克隆develop分支,这样

$ git branch -a # after clone should give
* develop
  remotes/origin/HEAD -> origin/develop
  remotes/origin/develop

2)按下

当开发人员使用命令git push origin develop将本地分支(develop)推送到远程存储库(origin/develop)时,我的理解是,更改已推送到origin/master如果 指向origin/master,与本评论中提到的不同


问题:

1) 在执行上述两项任务之前,是否建议在远程存储库中运行git remote set-head origin develop并设置HEAD指针?由任何开发人员

2) 不管远程存储库中的remotes/origin/HEAD值如何,git push origin develop都将更改推入origin/develop吗?

3) 我们在GitLab&之间使用webhook詹金斯. GitLab插件提供的env.gitlabSourceBranch是否给出了remotes/origin/HEAD指向的分支名称?如果是,如何获取发生推送事件的分支名称?通过webhook.

以下是在GitLab中完成的设置,以检索发生新提交的分支名称:

下面是Jenkins中的代码:

node('worker_node'){

    stage('stage1'){

        def repoName = env.gitlabSourceRepoName
        println "Repository Name: " + repoName // gives correct repo name


        def branchName = env.gitlabSourceBranch
        println "Branch name: " + branchName // gives always 'master' as value
   }
}

解决方案

建议运行git remote set-head origin develop并在远程存储库中设置HEAD指针...

这可能是语言问题,但是值得指出的是,该 并没有在远程存储库中设置HEAD.它对其他任何人的git clone命令没有影响.

这里的Git术语非常令人困惑.让我们分解一下:

  • remote 是您在Git存储库中使用的简称,用于引用其他一些Git存储库.因此,origin是远程的:它代表某个URL.如果您的Git拨打该URL,则其他一些Git会应答此电话".请注意,这里的 remote 是一个名词:它是一个独立的事物.

  • 远程存储库是您自己的存储库以外的其他存储库.当您使用Git拨打一些URL并调用其他Git时,其他Git是远程存储库.单词 remote 在这里是一个形容词.

  • 在Git中,单词 branch 相当模糊. (请参阅分支"到底是什么意思?)我更喜欢使用短语分支名称,其中 branch 是修饰形容词 name 的形容词,而远程跟踪名称(Git将此称为 remote-tracking分支name ),其中有一个完整的形容词短语,修改了 name 一词,分别表示masterorigin/master之类的东西.

    分支分支一词也可以表示一系列模糊的提交.在这种特殊情况下,我们使用git clone将Git存储库从某个URL复制到我们自己的机器上,或者使用git push将提交从计算机上的Git存储库提交到其他地方的其他Git存储库(可能在另一个机器)-我们将不需要这个词的其他含义.但是要知道它存在!

我们还需要一个或两个以上的Git术语:

  • 引用(或 ref )是分支名称,标记名称或其他类似名称.实际上,引用只是这些的概括:分支名称,标签名称和远程跟踪名称都是引用的特定种类.引用的完整拼写以refs/开头.

    通常紧随其后是其分类:例如,所有分支名称均以refs/heads/开头.实际上,这就是 how Git知道refs/heads/master 的一个分支.所有标签名称都以refs/tags/开头,这就是Git知道refs/tags/v1.2是标签的方式,所有远程跟踪名称都以refs/remotes/开头.

    在大多数情况下,您可以删除参考的refs/heads/refs/tags/refs/remotes/部分.例如,如果您只说master,则Git将搜索您所有的引用.如果您有一个refs/heads/master而没有一个refs/tags/master,则名称master必须是 that 分支名称,因此Git将以这种方式将其视为分支.同样,如果您只说v2.1,而Git搜索并找到了一个名为refs/tags/v2.1的标签,但没有别的,那么v2.1必须是那个标签名,因此Git将以这种方式对待它-作为标签.

    您的Git用来记住您的Git所调用的Git上的名称的所有远程跟踪名称均以refs/remotes/origin/开头.也就是说,您的Git将他们的 master记住为您的 origin/master.这样一来,如果您有另一个遥控器,那么您所指的 other master并不确定.例如,假设您添加了第二个遥控器(第三个Git存储库),简称为upstream.如果您调用upstream的URL上的Git有一个master分支,则您的Git将调用该upstream/master,这很容易告诉您,除了您的Git调用origin/master之外.

  • refspec 以其第二简单形式是一个参考对,中间有一个冒号:.因此,例如master:master是一个refspec. refs/heads/master:refs/remotes/origin/master也是如此.冒号左边的东西是 source ,冒号右边的东西是目的地.

git fetchgit push命令使用refspecs,稍后我们将看到.当您询问的是git clone而不是git fetch时,git clone的重要部分是它运行 git fetch.

牢记所有这些,接下来让我们看一下如何 Git解析符号名称,以及何时 Git解析符号名称.这部分有些棘手,因为git fetchgit push使用位置参数.

由于省去了很多细节(例如标志参数),因此在文档中将git pushgit fetch的参数说明如下:

       git fetch [repository] [refspec [refspec ...]]
       git push [repository] [refspec [refspec ...]]

也就是说,在pushfetch子命令之后,是 next 项,如果有下一项,根据定义, repository .根据定义, repository 参数中之后的所有项目都是 refspec 参数.您可以在存储库部分中输入原始URL,但通常应使用远程名称,这有两个原因:

  • 键入和正确输入更容易;和
  • 它启用了有用的特殊功能.

要插入任何 refspec 参数,您必须插入一个 repository 参数,因为如果没有该参数,Git只会认为您键入的 repository 自变量.也就是说,如果您不小心运行了git push master,Git将不会弄清楚您要键入git push origin master.它将仅尝试将master视为远程,否则将其视为URL.可能无论哪种方式都行不通,您将得到以下令人困惑的输出:

$ git push master
fatal: 'master' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

因此,对于git push,您可以选择以下选项:

git push
git push origin
git push origin somebranch
git push origin branch1:branch1 branch2:branch2 branch3 tag1:tag1

如前两个示例中所述,当您将东西遗漏时,Git为您选择远程和refspec(第一个示例),或者为您选择refspec(第二个示例).当包含一个refspec(第三个示例)或多个refspec(第四个示例)时,您将控制所有部分.

在回到第一个问题之前,让我们继续第二个问题:

git push origin develop是否将更改origin/develop推送到origin/develop,而不考虑远程存储库中的remotes/origin/HEAD值?

这包含多个术语错误(同样,它们可能只是语言问题).我认为总体思路很清楚,但是答案是肯定的:这完全忽略了remotes/origin/HEAD.

明确地说,这是

  • origin指定为远程,并且
  • develop指定为参考规范.

请记住,我们说的refspec的 second 最简单形式是名称的,中间带有冒号.它使用 most 简单形式,省略了第二个名称. pushfetch中省略第二个名字的行为是不同的.让我们只担心push在这里.

对于git push,省略refspec的第二部分意味着第一部分和第二部分相同.也就是说,develop 表示 develop:develop.名称develop本身很短-它不是以refs/开头的-但是您的Git和他们的Git都可能有一个名为develop分支并且没有 tag 名为develop. 1 因此,develop实际上是refs/heads/develop:refs/heads/develop的缩写.

请记住,他们的Git存储库.因此,它具有自己的 refs/heads/develop-自己的分支名称拼写为develop. 您的 refs/heads/develop标识您的存储库中的某些提交. 他们的 refs/heads/develop标识了他们的存储库中的某些提交.

运行git push时,您告诉您的 Git:连接到其他Git.然后通过给他们提供我不需要它们的任何提交,确保它们具有足够的提交和其他内部Git对象以实现最后一部分.最后,您已经完成了,要求他们(或命令他们)设置一些分支和/或标记名称,以指向某些特定的提交或其他适当的Git对象.

您将要求 them 设置的分支和/或标签名称来自refspec的 destination 部分.因此,如果您说git push ... develop:develop,则将让您的Git要求其Git更改的分支或标记名称是develop.您会要求他们将 commit 设置为它们,因为它们的develop是由源名称标识的名称-右侧的名称.因此,您将要求他们设置 develop以标识您的 develop所标识的同一提交.


1 如果要做同时拥有分支refs/heads/develop 标签refs/tags/develop,则会发生一些不太好的事情. Git对此有规定,但是Git的规定有些怪异和令人不安.最好的选择是完全避免这种情况:永远不要对标签和分支使用相同的简称.


他们可以说是或否

如果他们对此请求说是,则您的Git现在知道他们的develop代表该提交.您的Git通过更新您自己的origin/develop来记住他们的develop .因此,您的Git更新您的 origin/develop(您的refs/remotes/origin/develop,以使用其全名)来保存该提交哈希ID.

请注意,在此过程中,您的Git都没有看过自己的refs/remotes/origin/HEAD.您没有将refs/remotes/origin/HEAD放在refspec的左侧.您可以这样做.例如,您可以编写:

git push origin refs/remotes/origin/HEAD:refs/heads/develop

这将使您的Git将您的refs/remotes/origin/HEAD解析为提交哈希ID,通过origin调用Git,并要求那个 Git设置 refs/heads/develop到该提交哈希ID. 可能已经设置了该ID,所以这可能只是浪费时间,但是如果需要,您可以运行它.

您还可以运行:

git push origin HEAD:refs/heads/develop

会将您的HEAD解析为提交哈希ID,在origin处调用Git,并要求他们基于此设置分支develop;您可以运行:

git push origin a123456:refs/heads/develop

如果a123456是您的存储库中的某些有效提交.其中大多数形式都需要使用两部分的refspec,并在其中加上冒号,因为此处冒号左侧的东西根本不一定是分支名称,您将要问他们他们的 Git设置其分支名称之一.

但是,通常,使用git push时,您将以自己的一个或多个分支名称(例如develop和/或master)开头,并要求他们设置他们的相同名称的>分支名称.有时(例如,在您刚命名了新标签之后),您将希望让Git调用他们的Git,并要求他们将相同名称的新标签设置为相同的哈希ID.因此,git push的默认名称是将一个名称镜像为两个,因为您可以编写:

git push origin master develop v1.3

并因此要求他们一次性设置分支的 创建一个新标签.

如果他们拒绝,则可以零星地做..假设您要求他们设置所有这三个名称,并且他们接受了更新其develop并创建新的,但拒绝您更新其master的请求.您将获得两次成功,一次失败,Git会更新您的refs/remotes/origin/develop来记住分支成功,但不会更新您的refs/remotes/origin/master,因为分支失败意味着您根本不知道他们的master是什么. . (没有远程标签这样的东西,因此要求他们创建v1.3的成功或失败对存储库中的任何名称都没有影响.)

回到origin/HEAD:这是什么,它有什么好处?

简单地说(也许有点太夸张),refs/remotes/origin/HEAD是没用的.

它的 是您的Git的符号引用. 2 您可以随时使用git remote set-head origin对其进行设置.它最初是在git clone期间创建的.但据我所知,它没有任何实际用途.

我在上面提到过,您可以缩短参考文献:您可以说master表示refs/heads/masterv2.1表示refs/tags/v2.1.在您自己的Git存储库中尝试以下操作:运行git rev-parse并为其命名,以简短的名称表示分支和标签. (使用git branch列出您的分支,使用git tag列出您的标签.)您将看到类似这样的内容:

$ git rev-parse master
b5101f929789889c2e536d915698f58d5c5c6b7a
$ git rev-parse v2.1.0
7452b4b5786778d5d87f5c90a94fab8936502e20

rev-parse命令将从符号名称(如master)转换为哈希ID(如b5101f929789889c2e536d915698f58d5c5c6b7a). (它也可以从一个短的哈希ID转换为完整的ID,或执行其他许多技巧,但这是它的主要工作之一:将名称转换为哈希ID.)

通常,当您为Git命名时,Git会搜索所有引用以找出长名称. git rev-parse命令执行此操作,然后吐出哈希ID.大多数其他的Git命令 也可以这样做,但是随后以某种方式继续使用哈希ID.不过,在几乎所有情况下, 3 gitrevisions文档中都概述了此搜索过程,值得长期仔细研究.但是,现在快速浏览一下,然后向下滚动一点即可找到六步列表.

这里的六个步骤是如何 Git将短名称变成长名称.请注意,是在步骤3中尝试使用v2.1作为标记名:如果refs/tags/v2.1存在,则v2.1是标记名,这就是git rev-parse知道并找到其哈希ID的方式.步骤4尝试将master作为分支名称:如果refs/heads/master存在,则master是分支名称,这就是git rev-parse知道的方式.

在六步列表的底部,是第6步.这最后一步采用您键入的任何字符串(例如,可能是origin),并尝试将其作为refs/remotes/string/HEAD.如果存在,那一定是您的意思.因此,尽管origin通常是远程-它是您作为> git fetchgit push repository 参数键入的-它也是 /em> commit 的有效短名称,如果将其放在某些Git命令(如git rev-parse)将使用作为提交的地方. /p>

可以删除 refs/remotes/origin/HEAD,而git remote origin set-head -d完全可以做到这一点.如果已将其删除,则origin将与步骤6不匹配,并且git rev-parse origin将会失败.可以使用git remote origin set-head来将存储在refs/remotes/origin/HEAD中的名称​​更改到任何其他origin/*分支名称,以便第6步成功但使用该其他名称.这个 ever 都没有回到origin上的另一个Git!


2 我在这里掩盖符号参考.当诸如HEAD之类的名称包含另一个名称(而不是某些Git对象的哈希ID)时,就会发生符号引用. Git使用的机制本来是通用的,但充满了怪异的怪癖和缺陷,并且实际上仅对HEAD和(在较小程度上)这些远程跟踪的origin/HEAD样式名称有效. /p>

例如,您可以在分支名称空间中创建自己的符号引用.使用git symbolic-ref refs/heads/INDIR refs/heads/master创建名称INDIR作为符号引用分支.不幸的是,如果您这样做然后尝试删除名称INDIR,则Git会删除名称master!整个事情还没有真正准备好进行这种使用(滥用吗?).不要这样做!

3 我说几乎所有情况,因为某些Git命令知道他们的name参数是一个分支或标签名称,而其他 >怀疑.例如,git branch 知道,您将使用一个分支名称而不是标签名称作为下一个参数.因此,它不使用六步解析过程,实际上,它要求您在创建新分支时用完整的refs/heads/形式拼写一个分支名称.

git checkout命令最奇怪,因为允许checkout之后的位置参数(以及任何标志)为分支名称,标记名称或任何可解析为有效提交的名称.因此,似乎应该使用六步过程.如果创建一个分支并标记两个都名为X但指向两个不同的提交,则您可能希望git checkout X检出 tagged X.但是,实际上,它会检出分支.因此,虽然git checkout 尝试全部六个步骤,但它比步骤3尝试更早的步骤4 .

(从技术上讲,它尝试将名称作为分支名称时不会经过六步过程.相反,它只会首先尝试将其作为分支名称.如果可行,git checkout会将您置于如果失败,代码会调用 then 的六步名称到ID解析器,只要提交哈希返回,git checkout就会将您带到一个独立的因此,它实际上尝试两次执行步骤4,但是,如果它第一次失败,第二次也很可能失败.)


git clone的工作方式

运行git clone url时,您具有Git:

  1. 创建一个新的空目录(或接管一些现有的空目录).其余步骤全部发生在该目录中.
  2. 运行git init创建一个空的Git存储库.
  3. 运行git remote add创建一个遥控器.可以使用-o标志选择此遥控器的名称,但是如果您没有选择,则为origin.此新遥控器的URL是您提供给git clone的URL.
  4. git fetch设置一些默认的参考规格.实际的refspec取决于命令行标志,但是典型的标准是+refs/heads/*:refs/remotes/origin/*.请注意,这与我们在git push中使用的refspec非常相似.就像通过git remote set-head一样,为该遥控器配置符号HEAD.运行其他命令行标志指定的任何其他git config命令.
  5. 运行git fetch.这将使用在步骤3中创建的遥控器和在步骤4中设置的refspecs.
  6. 运行git checkout name.此步骤的 name 参数取决于命令行标志,它们取决于从另一个Git获得的信息.有关详情,请参见下文.

这是第6步,将新克隆放置在master上,或者放置在develop上,或者如果您在此处选择类似v2.1的任何东西,则甚至根本不在任何分支上.如果步骤6使您进入master分支,则创建您的master分支.如果步骤6使您拥有自己的develop分支,那么创建就是您的develop分支.如果步骤6留下一个分离的HEAD,那么您的Git不会创建任何分支!

如果愿意,可以使用git clone -b name指定最后一个克隆步骤应使用的名称.如果这样做的话,其他任何Git都没关系,当然,除了 name 必须匹配其名称之一.但是,如果您使用-b参数,那么(只有那时)其他Git所说的内容很重要.

另一个Git存储库 是一个Git存储库.这意味着其他Git存储库(为简单起见,将其称为服务器存储库)具有HEAD.服务器存储库中的HEAD指示在服务器存储库中检出了哪个分支.如果服务器存储库是--bare存储库,则它没有工作树,因此其HEAD有点无关紧要的.但是它仍然一个,这意味着仍然有一个服务器处于打开"状态,除非服务器的Git处于分离的HEAD模式(可能).

当您的Git调用服务器Git时,您的Git可以询问服务器的一件事是:您在哪个分支上?也就是说,您的Git可以向服务器询问有关的信息. >服务器的 HEAD.如果您在git clone期间未能为-b指定选定的分支,则那是您的Git将用于您的 git checkout的名称.

这也是您的Git用于git remote set-head origin --auto的名称,以及git clone将自动设置为origin/HEAD的名称.因此,服务器的 HEAD设置是git cloneorigin/HEAD副本的默认设置, 是最后一步命令.

这就是所有,这确实是件好事.如果您在git clone时用-b覆盖它,则的含义无关紧要,并且由于origin/HEAD还是毫无用处的,因此的含义就没有意义了.没关系.

结论

不用担心origin/HEAD.没用的.这对你没有任何好处.

不必担心很多关于服务器存储库中的HEAD.它确实会影响新的克隆,但前提是进行克隆的人员没有选择分支.如果确实要设置它,则可以在服务器允许的情况下进行设置. (不同的Web服务具有不同的设置或更改方式.)

最后,关于这个问题:

我们在GitLab&之间使用webhook詹金斯

我对您使用的特定插件一无所知.詹金斯(Jenkins)的文档各不相同:其中有些稍微有用,大部分似乎丢失,有些似乎只是错误的.通常,Jenkins会从您使用的任何托管服务器获取 的通知,表明发生了某些事情,然后编写代码对发生的事件进行解码.但是这部分:

如何获取发生推送事件的分支名称

从根本上来说是一个糟糕的问题,因为不一定有任何分支名称-例如,我们可能git push仅是标签-并且如果存在 分支名称,可能有许多分支名称,并且它们可能已更改或未更改哈希ID.正确的问题是让詹金斯(Jenkins)启动某些渠道是否合适.我真的不能帮忙 answer ,但这是您提出正确问题的指南.

If the developers are working on develop branch, for a new project

1) Clone

git clone <git_url> should be able to automatically clone develop branch locally without using -b option, so that

$ git branch -a # after clone should give
* develop
  remotes/origin/HEAD -> origin/develop
  remotes/origin/develop

2) Push

When developer pushes local branch(develop) changes to remote repository(origin/develop) using command git push origin develop, my understanding is, changes are pushed to origin/master, if remotes/origin/HEAD points to origin/master, unlike mentioned in this comment


Question:

1) Is it recommended to run git remote set-head origin develop and set HEAD pointer in remote repository, before performing above two tasks? by any developer

2) Does git push origin develop pushes changes origin/develop irrespective of remotes/origin/HEAD value in remote repository?

3) We use webhook between GitLab & Jenkins. Does env.gitlabSourceBranch provided by GitLab plugin, gives branch name that remotes/origin/HEAD point to? If yes, how to get the branch name on which, push event happen? through webhook.

Below are the settings done in GitLab, to retrieve branch name on which new commit occured:

Below is the code in Jenkins:

node('worker_node'){

    stage('stage1'){

        def repoName = env.gitlabSourceRepoName
        println "Repository Name: " + repoName // gives correct repo name


        def branchName = env.gitlabSourceBranch
        println "Branch name: " + branchName // gives always 'master' as value
   }
}

解决方案

Is it recommended to run git remote set-head origin develop and set HEAD pointer in remote repository ...

This might be a language issue, but it's worth pointing out here that this doesn't set HEAD in the remote repository. It has no effect on anyone else's git clone command.

Git's terminology here is very confusing. Let's break it down a bit:

  • A remote is a short name you use in your Git repository to refer to some other Git repository. Hence origin is a remote: it stands in for some URL. If your Git dials that URL, some other Git answers this "phone call". Note that the word remote here is a noun: it is a thing on its own.

  • A remote repository is a repository other than your own. When you have your Git dial some URL and call some other Git, that other Git is a remote repository. The word remote here is an adjective.

  • The word branch is rather ambiguous in Git. (See What exactly do we mean by "branch"?) I prefer to use the phrase branch name, where branch is an adjective modifying name, and remote-tracking name (Git calls this a remote-tracking branch name), which has an entire adjective phrase modifying the word name, to refer to things like master and origin/master respectively.

    The word branch can also mean a vaguely-defined series of commits. In this particular set of circumstances—where we use git clone to copy a Git repository from some URL to our own machine, or git push to send commits from our Git repository on our machine to some other Git repository elsewhere (probably on another machine)—we won't need this other sense of the word. But be aware that it exists!

We also need one more or two more pieces of Git terminology:

  • A reference (or ref) is a branch name, tag name, or other similar name. In fact, reference is just a generalization of these: branch names, tag names, and remote-tracking names are all just specific kinds of references. A reference has a full spelling that starts with refs/.

    This is usually immediately followed by its classification: for instance, all branch names start with refs/heads/. That, in fact, is how Git knows that refs/heads/master is a branch. All tag names start with refs/tags/, which is how Git knows that refs/tags/v1.2 is a tag, and all remote-tracking names start with refs/remotes/.

    In most cases you can drop the refs/heads/ or refs/tags/ or refs/remotes/ part of a reference. If you just say master, for instance, Git will search through all of your references. If you have a refs/heads/master and no refs/tags/master, the name master must be that branch name, so Git will treat it that way—as a branch. Likewise, if you just say v2.1, and Git searches and finds a tag named refs/tags/v2.1 but nothing else, then v2.1 must be that tag name, so Git will treat it that way—as a tag.

    All of the remote-tracking names that your Git uses to remember names on the Git your Git calls origin start with refs/remotes/origin/. That is, your Git remembers their master as your origin/master. This is so that if you have another remote, it's not ambiguous which other master you mean. Suppose, for instance, that you add a second remote—a third Git repository—that you call upstream for short. If the Git at the URL you call upstream has a master branch, your Git will call that upstream/master, and it's easy to tell this apart from the thing your Git calls origin/master.

  • A refspec is, in its second-simplest form, a pair of references with a colon : stuck between them. Hence master:master is a refspec, for instance. So is refs/heads/master:refs/remotes/origin/master. The thing on the left of the colon is the source, and the thing on the right of the colon is the destination.

The git fetch and git push commands use refspecs, as we will see in a moment. While you're asking about git clone rather than git fetch, a significant part of git clone is that it runs git fetch.

With all of this in mind, let's look next at how Git resolves a symbolic name, and when Git resolves a symbolic name. This part is a bit tricky because git fetch and git push use positional arguments.

Removing a lot of detail (such as flag arguments), the arguments to git push and git fetch are spelled out this way in the documentation:

        git fetch [repository] [refspec [refspec ...]]
        git push [repository] [refspec [refspec ...]]

That is, after the push or fetch sub-command, the next item, if there is a next item, is, by definition, a repository. Any items after the repository arguments are, by definition, refspec arguments. You can put a raw URL in for the repository part, but in general, you should use a remote name, for two reasons:

  • it's easier to type and get right; and
  • it enables useful special features.

To put in any refspec arguments, you have to insert a repository argument, because without one, Git will just think that whatever you typed is a repository argument. That is, if you accidentally run git push master, Git won't figure out that you meant to type git push origin master. It will just try to treat master as a remote, or failing that, as a URL. Probably it won't work either way and you'll get this puzzling output:

$ git push master
fatal: 'master' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

So with git push, you have options like this:

git push
git push origin
git push origin somebranch
git push origin branch1:branch1 branch2:branch2 branch3 tag1:tag1

When you leave stuff out, as in the first two examples, Git picks the remote and refspec for you (first example), or the refspec for you (second example). When you include a refspec (third example) or multiple refspecs (fourth example), you control all the parts.

Let's go on to your second question now, before we come back to the first:

Does git push origin develop pushes changes origin/develop irrespective of remotes/origin/HEAD value in remote repository?

This contains multiple terminology errors (again they might just be language issues). The overall idea is, I think, clear enough, though, and the answer is yes: this totally disregards remotes/origin/HEAD.

To be explicit, this:

  • specifies origin as the remote, and
  • specifies develop as the refspec.

Remember that we said that the second simplest form of refspec is a pair of names with a colon between them. This uses the most simple form, which omits the second name. The behavior of omitting the second name is different in push and fetch. Let's only worry about push here.

For git push, omitting the second part of a refspec means first part and second part are the same. That is, develop means develop:develop. The name develop itself is short—it doesn't start with refs/—but both your Git and their Git probably have a branch named develop and no tag named develop.1 So develop is actually short for refs/heads/develop:refs/heads/develop here.

Remember that their Git repository is a Git repository. It therefore has its own refs/heads/develop—its own branch name spelled develop. Your refs/heads/develop identifies some commit in your repository. Their refs/heads/develop identifies some commit in their repository.

When you run git push, you tell your Git: Connect to some other Git. Then make sure they have enough commits and other internal Git objects to achieve the last part, by giving them any commits I have that they don't that they will need. Last, you've finished that, ask them—or command them—to set some of their branch and/or tag names to point to some particular commits or other appropriate Git objects.

The branch and/or tag names that you will ask them to set come from the destination part of the refspec. So if you said git push ... develop:develop, the branch or tag name you'll have your Git ask their Git to change is develop. The commit you'll ask them to set as their develop is the one identified by the source name—the one on the right. So you're going to ask them to set their develop to identify the same commit that your develop identifies.


1If you do have both a branch refs/heads/develop and a tag refs/tags/develop, some not-very-good things happen. Git has rules for this, but Git's rules are a bit weird and unsettling. Your best bet is to avoid this situation entirely: never use the same short name for both a tag and a branch.


They can say either yes or no

If they say yes to this request, your Git now knows that their develop represents that commit. Your Git remembers their develop by updating your own origin/develop. So your Git updates your origin/develop—your refs/remotes/origin/develop, to use its full name—to save that commit hash ID.

Note that nowhere in this process did your Git look at your own refs/remotes/origin/HEAD. You didn't put refs/remotes/origin/HEAD on the left side of your refspec. You could do that if you wanted. For instance, you could write:

git push origin refs/remotes/origin/HEAD:refs/heads/develop

This will have your Git resolve your refs/remotes/origin/HEAD to a commit hash ID, call up the Git over at origin, and ask that Git to set their refs/heads/develop to that commit hash ID. It probably already is set to that ID so this is probably just a waste of time, but it is something you could run, if you wanted.

You can also run:

git push origin HEAD:refs/heads/develop

which resolves your HEAD to a commit hash ID, calls up the Git over at origin, and asks them to set their branch develop based on that; and you can run:

git push origin a123456:refs/heads/develop

if a123456 is some valid commit in your repository. Most of these forms require the two-part refspec, with the colon in it, because the thing on the left of the colon here is not necessarily a branch name at all, and you're going to want to ask their Git to set one of their branch names.

In general, though, when using git push, you will start with your own branch name or names (like develop and/or master) and want to ask them to set their branch names, of the same name, to the same commit hash ID. Occasionally—such as after you've just made a new tag name—you will want to have your Git call up their Git and ask them to set a new tag of the same name, to the same hash ID. So git push's default of mirroring the one name into two serves you well, because you can just write:

git push origin master develop v1.3

and thereby ask them to set both of their branches, and create a new tag, all in one go.

If they say no, they can do it piecemeal. Suppose you ask them to set all three of these names, and they accept your request to update their develop and create a new v1.3, but reject your request to update their master. You'll get two successes one one failure, and your Git will update your refs/remotes/origin/develop to remember the branch success, but not update your refs/remotes/origin/master because the branch failure means you don't know what their master is after all. (There is no such thing as a remote tag so success or failure at asking them to create v1.3 has no effect on any names in your repository.)

Back to origin/HEAD: what is it and what good is it?

To put it briefly (and maybe slightly too aggressively), refs/remotes/origin/HEAD is useless.

What it is is a symbolic reference that your Git has.2 You can set it, any time you like, using git remote set-head origin. It's initially created during git clone. But it has, as far as I can tell, no practical purpose whatsoever.

I mentioned above that you can shorten references: you can say master to mean refs/heads/master and v2.1 to mean refs/tags/v2.1. Try this sort of thing in your own Git repository: run git rev-parse and give it short and long names for branches and tags. (Use git branch to list your branches, and git tag to list your tags.) You will see things like this:

$ git rev-parse master
b5101f929789889c2e536d915698f58d5c5c6b7a
$ git rev-parse v2.1.0
7452b4b5786778d5d87f5c90a94fab8936502e20

The rev-parse command translates from a symbolic name, like master, to a hash ID, like b5101f929789889c2e536d915698f58d5c5c6b7a. (It can also translate from a short hash ID to a full one, or do many other tricks, but that's one of its main jobs: to turn a name into a hash ID.)

In general, when you give Git a short name, Git searches through all your references to figure out what the long one is. The git rev-parse command does this and then spits out the hash ID. Most other Git commands also do this, but then go on to use the hash ID in some way. In almost all cases, though,3 this searching process is outlined in the gitrevisions documentation, which is worthy of close and careful study over time. But take a quick look at it now and scroll down just a little bit to find a six-step list.

The six-step list here is how Git turns a short name into a long one. Note that it's step 3 that tries v2.1 as a tag name: if refs/tags/v2.1 exists, then v2.1 is a tag name, and that's how git rev-parse knows and finds its hash ID. It's step 4 that tries master as a branch name: if refs/heads/master exists, then master is a branch name and that's how git rev-parse knows.

Down at the bottom of the six-step list, there is a step 6. This last step takes whatever string you typed in—which could be origin, for instance—and tries it as refs/remotes/string/HEAD. If that exists, that must be what you meant. So while origin is typically a remote—it's what you type in as the repository argument for git fetch and git push—it's also a valid short name for a commit, if you put it in a place where some Git command, like git rev-parse, will use it as a commit.

It's possible to remove refs/remotes/origin/HEAD and git remote origin set-head -d does exactly that. If you have removed it, origin won't match step 6 and git rev-parse origin will just fail. It's possible to use git remote origin set-head to change the name stored in your refs/remotes/origin/HEAD to any other origin/* branch name, so that step 6 succeeds but uses that other name. None of this ever goes back to the other Git over at origin!


2I'm glossing over symbolic reference here. A symbolic reference occurs when a name like HEAD contains, instead of the hash ID of some Git object, another name. The mechanism that Git uses is intended to be general, but is full of weird quirks and flaws, and actually really only works right with HEAD, and—to a lesser extent—with these remote-tracking origin/HEAD style names.

You can, for instance, create your own symbolic references in the branch name space. Using git symbolic-ref refs/heads/INDIR refs/heads/master creates the name INDIR as a symbolic reference branch. Unfortunately, if you do this and then attempt to delete the name INDIR, Git deletes the name master instead! The whole thing is not really ready for this sort of use (abuse?). Don't do it!

3I say almost all cases because certain Git commands know that their name argument is a branch or tag name, and others suspect it. For instance, git branch knows that you are going to give it a branch name, not a tag name, as the next argument. So it doesn't use the six-step resolution process, and in fact, it demands that you not spell a branch name with the full refs/heads/ form when you create a new branch.

The git checkout command is the weirdest, because the positional argument after checkout (and any flags) is allowed to be a branch name, a tag name, or anything that resolves to a valid commit. So it seems like it should use the six-step process. If you create a branch and tag both named X but pointing to two different commits, you might expect git checkout X to check out the the tagged X. In fact, though, it checks out the branch. So while git checkout will try all six steps, it tries step 4 earlier than step 3.

(Technically, it isn't going through the six-step process when it tries the name as a branch name. Instead, it just tries it as a branch name first. If that works, git checkout puts you on the branch and is done. If it fails, then the code calls the six-step name-to-ID-resolver, and as long as a commit hash comes back, git checkout puts you on a detached HEAD on that commit. So it actually tries step 4 twice, as it were—but if it failed the first time, it's extremely likely to fail the second time too.)


How git clone works

When you run git clone url, you have Git:

  1. Create a new, empty directory (or take over some existing empty directory). The remaining steps all happen in that directory.
  2. Run git init to create an empty Git repository.
  3. Run git remote add to create a remote. The name of this remote is whatever you chose with your -o flag, but if you didn't choose one, it's just origin. The URL for this new remote is the URL you gave to git clone.
  4. Set some default refspecs for git fetch. The actual refspecs depend on command-line flags, but a typical standard one is +refs/heads/*:refs/remotes/origin/*. Note how closely this resembles the refspecs we were using for git push. Configure the symbolic HEAD for this remote as if by git remote set-head. Run any additional git config commands specified by additional command-line flags.
  5. Run git fetch. This uses the remote created in step 3 and the refspecs set in step 4.
  6. Run git checkout name. The name argument to this step depends on command line flags and on information obtained from the other Git. See below for details.

It's step 6 that puts your new clone on master or, perhaps, on develop, or even on no branch at all if you chose something like v2.1 here. If step 6 puts you on your master branch, that's what creates your master branch. If step 6 puts you own your develop branch, that's what creates your develop branch. If step 6 leaves you with a detached HEAD, your Git doesn't create any branches!

You can, if you like, specify which name this last clone step should use, using git clone -b name. If you do that, nothing the other Git says matters, except of course that name has to match one of its names. But if you don't use a -b argument, then—and only then—what the other Git says matters a lot.

The other Git repository is a Git repository. This means the other Git repository—let's call it the server repository for simplicity—has a HEAD. The HEAD in the server repository tells which branch is checked-out in the server repository. If the server repository is a --bare repository, it has no work-tree, so its HEAD is somewhat irrelevant. But it still has one, and that means there's still one that the server is "on", unless the server's Git is in detached HEAD mode (which is possible).

When your Git calls up the server Git, one of the things your Git can ask the server is: Which branch are you on? That is, your Git can ask the server about the server's HEAD. If you fail to specify a chosen branch for -b during git clone, that's the name that your Git will use for your git checkout.

That's also the name that your Git will use for your git remote set-head origin --auto, and the name that your git clone will set up automatically as your origin/HEAD. So the server's HEAD setting is the default for your origin/HEAD copy at git clone time, and the default for the last-step-of-clone git checkout command.

That's all it really is good for. If you override it with -b at git clone time, that meaning doesn't matter, and since origin/HEAD is quite useless anyway, that meaning doesn't matter either.

Conclusion

Don't worry about origin/HEAD. It's useless. It doesn't do you any good.

Don't worry much about HEAD in the server repository. It does affect new clones, but only if the person doing the clone doesn't pick a branch. If you do want to set it, you can do that however your server allows. (Different web services have different ways to set or change this.)

Last, regarding this question:

We use webhook between GitLab & Jenkins

I know nothing about the particular plugin you are using. Jenkins documentation varies: some of it is slightly useful, most of it seems to be missing, and some of it seems to be just plain wrong. In general Jenkins gets notifications from whatever hosting server you use, that something has happened, and then you write code that decodes the event(s) that occurred. But this part:

how to get the branch name on which push event happen(s)

is a fundamentally bad question, because there isn't necessarily any branch name—we might git push to a tag only, for instance—and if there is a branch name, there might be many branch names, and they might have changed hash IDs, or not. The right question to ask is whether it's appropriate to have Jenkins start some pipeline(s). I can't really help answer that, but this is your guide to asking the right questions.

这篇关于遥控器/原点/HEAD应设置为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆