如何推广/ ff分支到HEAD

时间:2018-02-24 22:58:26

标签: git tfs

我有一个受保护的分支,只有在集成构建的集成构建通过后才能提升/快速转发。

我目前尝试通过集成构建对集成分支的拉取请求来解决这个问题,一旦成功,只需将发布分支快速转发到集成分支的顶端。

但是,当我在TFS构建系统上构建分支时,它将检查集成分支头部的提交,使构建服务器处于分离头状态。

这一切都应该没问题,但出于某种原因,如果不使用多个语句,我就不能这样做。对我来说,直观地说,应该有一个简单的命令。我只想将分支点快进到当前提交。

现在,我找到了两种方法:

使用脚本

保存HEAD提交
  1. 将HEAD提交保存为当前shell脚本语言中的变量(此处已保存到%HEAD_commit_id%)
  2. git checkout release
  3. git merge %HEAD_commit_id%
  4. 使用一次性分支

    1. git branch current-head
    2. git checkout release
    3. git merge current-head
    4. git branch -d current-head
    5. 寻找更好的解决方案

      以上是"最好的&#34>是否正确?我能做到这一点吗?我认为这个存在一条线?解决方案1和解决方案2都有其警告,所以我宁愿不同意,但我最终可能会最终使用数字2。

      重现场景的步骤

      要重现我所拥有的确切场景,您需要2次提交(C1和C2)和2次分支(发布和集成)。

      点释放到C1,集成到C2,HEAD到C2(分离头状态)。

      最终结果应该允许我推送发布分支,现在指向与集成相同的提交。

1 个答案:

答案 0 :(得分:5)

您有许多选项;使用哪个取决于您可以保证的环境。

你可以使用(帽子的尖端到jthill):

git checkout release && git merge --ff-only @{1}

(使用shell需要保护扩展的任何东西)。这相当于您的第一种方法,但不需要临时变量,因为@{1}表示"结帐前的HEAD之前的值"。

但是,您也可以使用这些相当棘手的操作之一:

git push . HEAD:refs/heads/release

或:

git fetch . HEAD:refs/heads/release

我们将在下面看到。这在某些方面更有效。它的主要缺点是几乎没有人groks

背景

让我们首先注意每个存储库都是自包含的,我的意思是每个存储库都有自己的分支名称,以及它自己的HEAD和索引和工作树。 (后三个 - HEAD,索引和工作树功能作为一个单元。如果你有Git 2.5或更高版本,你可以使用git worktree add添加独立的工作树,每个工作树带有自己的HEAD和索引。)

git checkout name-or-ID,假设一切顺利:

  1. HEAD指向ID,通常是通过 name ("附加的HEAD"),但必要时直接指向--detach你给它一个ID,或使用name确保它从你给它的 HEAD 中分离HEAD。
  2. 从提交git merge现在名称填写索引。
  3. 从同一次提交中填写工作树。
  4. 现在,让我们考虑一下HEAD在快进而不是实际合并时的作用。当git merge命名作为目标提交的祖先的提交并且您告诉git merge与标识该目标提交的任何内容合并时,会发生这种情况。您传递给%HEAD_commit_id%的标识符可以是原始哈希ID(如git merge current-head方法中所示),也可以是分支名称(如git merge方法中所示)。唯一必要的是HEAD

    1. 找到目标提交(例如,接收其哈希ID或将名称解析为其哈希ID);
    2. 计算HEAD和目标提交的合并基础;和
    3. 发现此合并基础与git merge --no-ff标识的提交相同。
    4. 这些是可以进行快进合并的条件。如果可以进行快进合并,您没有明确禁止它(git merge -s ours)或隐式(git merge,或者使用带注释的标签进行合并的某些情况),--ff-only将在这种情况下执行快进操作。您甚至可以将git merge添加到git merge的参数中,告诉Git:如果可能的话,快进,如果不可能,请给我一个错误,而不是做真正的合并。

      快进合并操作

      现在让我们来看看快进合并实际上做了些什么。这有多个部分,因为HEAD会影响当前(HEAD-attached)分支,或者HEAD分离,HEAD本身。请注意,如果出现问题,Git会尝试退出整个操作,这样看起来一切都成功,或者快进甚至从未开始。 (在一些特别令人讨厌的情况下,例如当计算机着火时,如果你的计算机在大火中幸存下来,那么凌乱的内部状态可能会显示出来。)

      • Git需要使索引和工作树匹配合并结果,这当然是目标提交。所以实际上,Git会检出目标提交。但是,HEAD仍然连接到当前分支,如果它已连接。此目标提交的检出会更新索引和工作树。

      • 如果附加HEAD,Git现在会更改附加...--o--o <-- branch (HEAD) \ o--o--o <-- target_commit \ o--... 的分支名称,以便此名称标识刚签出的提交 - 目标提交。也就是说,如果我们有一些东西,我们可以这样画:

        ...--o--o
                 \
                  o--o--o   <-- branch (HEAD), target_commit
                         \
                          o--...
        

        我们现在有:

        HEAD

        当我们在指定的分支上时,这是游戏中的快进操作。

        (如果branch已分离,只需想象前面没有名称git fetch的相同操作。)

      考虑git pushgit fetch

      当您使用git push从另一个Git获取新提交,或git fetch将您自己的提交提交给另一个Git时,您可以让两个Gits相互通信并传输任何新提交。假设这是...--o--o--X 操作:

      ...--o--o--X--Y--Z
      

      变为:

      Y--Z

      其中X是与我们分支的提示提交X链接的新提交。通常情况下,master在操作的 start 处有两个指向它的名称,例如origin/master...--o--o--X <-- master (HEAD), origin/master

      origin/master

      最后,相应的...--o--o--X <-- master (HEAD) \ Y--Z <-- origin/master 名称已移动,因此我们可能会提取这样的提交:

      origin/master

      考虑名称branch如何移动,并将其与我们进行快进合并时origin/master移动的方式进行比较。 这些是相同的动作:名称X以快进方式移动,因此它不是指向提交Z,而是指向提交git push }。

      这也是master期间发生的事情,除了我们的Git没有让我们的Git从他们的Git中检索提交,我们提交了他们的Git提交。但是一旦我们这样做了,我们就不希望我们的 Git记住他们的 Git的主人,我们想要他们的 Git要记住,作为他们的 ...--o--o--X <-- master ,我们的最终提交。所以如果他们有:

      --Y--Z

      我们给了他们X连接到master,我们希望他们他们的 Z指向...--o--o--X--Y--Z <-- master

      ...--o--o--X--W   <-- master
                  \
                   Y--Z   <-- (we request that they set their master here)
      

      这也是快进操作。

      请注意,如果我们提出的请求不是快进,则其他Git通常拒绝我们的请求:

      master

      在这种情况下,如果他们将名称Z移到指向W,他们就会忘记&#34;提交git merge。这是非快进操作,并且当我们运行git push时,会导致我们获得真正的合并。正在接收git merge 的Git赢了运行--force,因此要求我们的请求是快进操作 - 好吧,除非我们告诉强制改变,否则需要这样做。

      只要我们不使用git push或同等效果,这种git push就不会做出任何不是快进操作的更改。 The other Git will simply say: No, I won't do that, because it's not a fast-forward.

      请注意,与我们的快进合并案例不同,他们的Git - 我们git checkout ing- 的其他Git并不会运行--bare更新的提交哈希值。这就是为什么我们必须推送到没有工作树的git push存储库,或推送到另一个Git没有检出的分支。 1 < / p>

      这意味着我们可以(ab)使用fetch或push

      我们知道:

      1. release会快进一个分支,只要它没有签出,
      2. 我们在&#34;分离的HEAD&#34;模式,所以我们没有签出分支release,所以
      3. 如果我们让Git打电话给我们自己的Git怎么办?如果我们要求他们将他们的 release设置为某个特定的提交哈希,该怎么办?
      4. 我们的Git会问他们的Git - 这是我们的Git,只是戴着不同的帽子 - 接受任何新的提交 - 没有任何东西;我们的Git拥有我们Git所拥有的所有提交 - 然后以快进的方式更新它(我们的)release,因为我们并没有要求它强制执行任何操作。因此,如果我们只是说:嘿,其他Git,我们可以将我们自己的HEAD快进到refs/heads/release标识的提交,将HEAD设置为我HEAD:refs/heads/release 确定的提交{1}}!我们使用以下格式的 refspec 进行操作:

        .

        现在唯一的伎俩是打电话给自己。通常我们可能需要一个URL,但Git允许路径名,包括相对路径名;相对路径名git push . HEAD:refs/heads/release 表示当前目录。所以,如果我们:

        refs/heads/release

        我们让我们的Git调用自己的Git,不传输任何内容(没有新的提交),并要求我们的Git以快进的方式设置我们的HEAD,以匹配我们当前的{{ 1}}。由于我们的release未签出,因此当且仅当操作是快进时才允许这样做。

        获取技巧完全相同:我们从自己获取(不传输任何提交),然后请求我们自己的Git将我们自己的refs/heads/release设置为特定的提交哈希。由于没有前导+强制标志,这也验证了操作仅快进。由于我们不使用--update-head-ok,因此我会执行与git push相同的检查,即当前HEAD不会将分支命名为release。奇怪的是,这次我们从其他 Git中获取了哈希ID(但是我们自己也是这样,无论如何都是相同的哈希ID)。

        1 这实际上取决于receive.denyCurrentBranch的设置。另见receive.denyNonFastForwards。较新的Gits也支持updateInstead我们可以使用目标Git运行git checkout,但仅限于某些情况;这允许非裸存储库与git push一起使用,即使分支占用其工作树和索引对。