【问题标题】:What should remotes/origin/HEAD set to?remotes/origin/HEAD 应该设置为什么?
【发布时间】:2019-07-31 16:19:17
【问题描述】:

如果开发人员正在 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,如果 @987654334 @指向origin/master,不像这个comment中提到的


问题:

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

2) git push origin develop 是否会推送更改 origin/develop 而不管远程存储库中的 remotes/origin/HEAD 值如何?

3) 我们在 GitLab 和 Jenkins 之间使用 webhook。由GitLab plugin 提供的env.gitlabSourceBranch 是否给出了remotes/origin/HEAD 指向的分支名称?如果是,如何获取发生推送事件的分支名称?通过网络钩子。

以下是在 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
   }
}

【问题讨论】:

  • 为什么要将开发推送到远程而不是保留master?更好地掌握和发展。如果还有其他业务需要我知道我会指导你。在这里,您可以将您当前的分支重命名为 git branch -m master 之类的 master 并结帐到新分支 git checkout -b develop 现在将您的开发分支推送到远程服务器 git push origin develop -u 这将上游设置为开发分支。
  • 如果你使用 git clone 它会自动设置远程来源。如果您已经开始从 git init 制作本地 git repo 或者您想更改远程 url,您只需要设置远程 url。如果您想创建本地分支,请使用 git checkout -b <branchName>。 git checkout <branch> 将从远程提取分支(如果存在)。最好在 checkout 之前运行 git fetch 以获取新创建的分支的信息 在 jeckins 中,您不需要设置远程 url,它只需要分支名称和 repo 的基本 url。如果你清楚,我知道

标签: git jenkins gitlab jenkins-plugins git-remote


【解决方案1】:

是否建议在远程存储库中运行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是形容词。

  • branch 这个词在 Git 中相当含糊。 (见What exactly do we mean by "branch"?)我更喜欢使用短语branch name,其中branch是修饰name的形容词,而remote- tracking name(Git 称之为 remote-tracking branch name),它有一个完整的形容词短语来修饰单词 name,来指代诸如 @987654328 之类的东西分别是@和origin/master。

    branch 这个词也可以表示定义模糊的一系列提交。在这组特殊的情况下——我们使用git clone 将 Git 存储库从某个 URL 复制到我们自己的机器,或使用 git push 将提交从我们机器上的 Git 存储库发送到其他地方的其他 Git 存储库(可能在另一个机器)——我们不需要这个词的其他含义。但请注意它的存在!

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

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

    通常紧跟其后的是其分类:例如,所有分支名称都以refs/heads/ 开头。事实上,这就是如何 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 必须是那个 分支名称,因此Git 会将其视为一个分支。同样,如果你只说v2.1,Git 搜索并找到一个名为refs/tags/v2.1 的标签,但没有别的,那么v2.1 必须是那个标签名称,所以Git 会这样对待它——作为标签。

    您的 Git 用于记住 Git 上的名称的所有远程跟踪名称,您的 Git 调用 origin 以 refs/remotes/origin/ 开头。也就是说,您的 Git 将 他们的 master 记住为 您的 origin/master。这样一来,如果您有另一个遥控器,那么 other master 您的意思就不会模棱两可了。例如,假设您添加了第二个远程(第三个 Git 存储库),简称为 upstream。如果您调用upstream 的URL 上的Git 有一个master 分支,那么您的Git 将调用它upstream/master,这很容易区分您的Git 调用origin/master。

  • refspec 是第二个最简单的形式,是一对引用,它们之间有一个冒号:。例如,master:master 是一个参考规范。 refs/heads/master:refs/remotes/origin/master 也是如此。冒号左边是source,冒号右边是destination。

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

考虑到所有这些,接下来让我们看看Git 如何解析符号名称,以及何时Git 解析符号名称。这部分有点棘手,因为git fetch 和git push 使用位置参数。

去掉很多细节(例如标志参数),git push 和 git fetch 的参数在文档中是这样拼写的:

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

即在push或fetch子命令之后,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(第三个示例)或多个 refspecs(第四个示例)时,您可以控制所有部分。

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

git push origin develop 是否会推送更改 origin/develop 而不管远程存储库中的 remotes/origin/HEAD 值如何?

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

明确地说,这是:

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

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

对于git push,省略参考规范的第二部分意味着第一部分和第二部分相同。也就是说,develop 表示 develop:develop。 develop 这个名字本身很短——它不是以refs/ 开头的——但是你的 Git 和他们的 Git 可能都有一个名为 develop 的 branch 并且没有 tag 命名为develop。1 所以develop 在这里实际上是refs/heads/develop:refs/heads/develop 的缩写。

请记住,他们的 Git 存储库是一个 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。您将要求他们设置为develop 的commit 是由源名称标识的— 右侧的那个。因此,您将要求他们设置 他们的 develop 来识别 your develop 识别的相同提交。


1如果你确实有一个分支refs/heads/develop和一个标签refs/tags/develop,一些不太好的事情会发生. Git 有这方面的规则,但 Git 的规则有点奇怪和令人不安。最好的办法是完全避免这种情况:永远不要对标签和分支使用相同的短名称。


他们可以说是或否

如果他们同意此请求,您的 Git 现在知道他们的 develop 代表该提交。您的 Git 记住他们的 develop 通过更新您自己的 origin/develop。所以你的 Git 更新你的origin/develop——你的refs/remotes/origin/develop,使用它的全名——来保存那个提交哈希ID。

请注意,在此过程中,您的 Git 没有查看您自己的 refs/remotes/origin/HEAD。您没有将refs/remotes/origin/HEAD 放在参考规范的左侧。如果您愿意,可以这样做。例如,你可以写:

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)并希望他们设置他们的 相同名称的分支名称,指向相同的提交哈希 ID。有时——比如在你刚刚创建了一个新的标签名称之后——你会想让你的 Git 调用他们的 Git 并要求他们设置一个同名的新标签,到相同的哈希 ID。所以git push 将一个名字镜像成两个的默认设置很适合你,因为你可以写:

git push origin master develop v1.3

然后要求他们设置两个他们的分支,和创建一个新标签,一气呵成。

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

回origin/HEAD:这是什么东西,有什么好处?

简而言之(也许有点过于激进),refs/remotes/origin/HEAD 是没用的。

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

我在上面提到你可以缩短引用:你可以说master 表示refs/heads/master 和v2.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。但是,在几乎所有情况下,3the gitrevisions documentation 中概述了此搜索过程,值得随着时间的推移进行仔细和仔细的研究。但是现在快速浏览一下,向下滚动一点,找到一个六步列表。

这里的六步列表是如何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/<em>string</em>/HEAD。如果存在,那一定是你的意思。因此,虽然origin 通常是一个远程——它是您作为git fetch 和git push 的repository 参数输入的——它也是 /em> commit 的有效短名称,如果您将它放在某个 Git 命令(如 git rev-parse)将 使用它作为提交的地方。

可以删除 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 步就会成功,但会使用其他名称。这些永远都不会回到origin的另一个Git!


2我在这里掩盖了符号引用。当像 HEAD 这样的名称包含另一个名称而不是某个 Git 对象的哈希 ID 时,就会发生符号引用。 Git 使用的机制是通用的,但充满了奇怪的怪癖和缺陷,实际上只适用于 HEAD,并且在较小程度上适用于这些远程跟踪 origin/HEAD 样式名称。

例如,您可以在分支名称空间中创建自己的符号引用。使用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 检出标记X。但事实上,它检查了分支。因此,虽然git checkout 将尝试所有六个步骤,但它比步骤 3 更早尝试步骤 4。

(从技术上讲,当它尝试将名称作为分支名称时,它不会经历六步过程。相反,它只是先尝试​​将其作为分支名称。如果可行,git checkout 会将您置于分支并完成。如果失败,然后代码调用六步 name-to-ID-resolver,只要返回提交哈希,git checkout 就会将您置于分离状态HEAD 在那个提交上。所以它实际上尝试了第 4 步两次,但如果第一次失败,那么第二次也极有可能失败。)


git clone 的工作原理

当你运行 git clone <em>url</em> 时,你就有了 Git:

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

这是第 6 步,将您的新克隆放在 master 上,或者可能放在 develop 上,或者如果您在此处选择 v2.1 之类的东西,甚至根本不放在任何分支上。如果第 6 步将您置于您的 master 分支上,这就是 创建您的 master 分支的原因。如果第 6 步让您拥有您的 develop 分支,那么这就是 创建 您的 develop 分支。如果第 6 步留下一个分离的 HEAD,那么您的 Git 不会创建任何分支!

如果您愿意,可以使用git clone -b <em>name</em> 指定最后一个克隆步骤应使用的名称。如果您这样做,其他 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设置是origin/HEAD在git clone时间复制的默认设置,和克隆的最后一步@987654545的默认设置@ 命令。

这就是 所有 它确实有好处。如果您在git clone 时间用-b 覆盖它,那个 的意思并不重要,而且由于origin/HEAD 无论如何都没用,那个 的意思并不重要也无所谓。

结论

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

不用担心服务器存储库中的HEAD太多。它确实会影响新的克隆,但前提是进行克隆的人没有选择分支。如果您确实想设置它,您可以按照您的服务器允许的方式进行设置。 (不同的 Web 服务有不同的方法来设置或更改它。)

最后,关于这个问题:

我们在 GitLab 和 Jenkins 之间使用 webhook

我对您使用的特定插件一无所知。 Jenkins 文档各不相同:其中一些有点用处,大部分似乎丢失了,还有一些似乎完全错误。一般来说,无论您使用什么托管服务器,Jenkins 都会收到通知,告诉您发生了什么事,然后您编写代码来解码发生的事件。但是这部分:

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

是一个根本不好的问题,因为不一定有 any 分支名称——例如,我们可能只对一个标签 git push——如果有 一个分支名称,可能有 许多 个分支名称,并且它们可能已经更改了哈希 ID,或者没有。要问的正确问题是让 Jenkins 启动一些管道是否合适。我真的无法回答这个问题,但这是你提出正确问题的指南。

【讨论】:

  • 对于最后一段,在您的回答中,为第三个问题编辑了查询
  • 看起来 env.gitlabSourceBranch 在那个特定的 Jenkins 脚本中没有设置任何有用的东西。我从来没有找到任何足够的 Jenkins 文档——它太糟糕了,以至于我很惊讶有人使用它,因为它有点像一辆有三个脚踏板的汽车,没有一个是刹车踏板,没有方向盘,也没有说明如何驾驶它...
猜你喜欢
  • 2013-09-25
  • 2021-03-07
  • 2012-09-18
  • 1970-01-01
  • 2021-03-29
  • 2018-01-30
  • 1970-01-01
  • 2016-09-05
  • 2014-06-08
相关资源
最近更新 更多