我有一个受保护的分支,只有在集成构建的集成构建通过后才能提升/快速转发。
我目前尝试通过集成构建对集成分支的拉取请求来解决这个问题,一旦成功,只需将发布分支快速转发到集成分支的顶端。
但是,当我在TFS构建系统上构建分支时,它将检查集成分支头部的提交,使构建服务器处于分离头状态。
这一切都应该没问题,但出于某种原因,如果不使用多个语句,我就不能这样做。对我来说,直观地说,应该有一个简单的命令。我只想将分支点快进到当前提交。
现在,我找到了两种方法:
git checkout release
git merge %HEAD_commit_id%
git branch current-head
git checkout release
git merge current-head
git branch -d current-head
以上是"最好的&#34>是否正确?我能做到这一点吗?我认为这个存在一条线?解决方案1和解决方案2都有其警告,所以我宁愿不同意,但我最终可能会最终使用数字2。
要重现我所拥有的确切场景,您需要2次提交(C1和C2)和2次分支(发布和集成)。
点释放到C1,集成到C2,HEAD到C2(分离头状态)。
最终结果应该允许我推送发布分支,现在指向与集成相同的提交。
答案 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
,假设一切顺利:
HEAD
指向ID
,通常是通过 name
("附加的HEAD"),但必要时直接指向--detach
你给它一个ID,或使用name
确保它从你给它的 HEAD
中分离HEAD。git merge
现在名称填写索引。现在,让我们考虑一下HEAD
在快进而不是实际合并时的作用。当git merge
命名作为目标提交的祖先的提交并且您告诉git merge
与标识该目标提交的任何内容合并时,会发生这种情况。您传递给%HEAD_commit_id%
的标识符可以是原始哈希ID(如git merge current-head
方法中所示),也可以是分支名称(如git merge
方法中所示)。唯一必要的是HEAD
:
HEAD
和目标提交的合并基础;和git merge --no-ff
标识的提交相同。这些是可以进行快进合并的条件。如果可以进行快进合并,和您没有明确禁止它(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 push
和git 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>
我们知道:
release
会快进一个分支,只要它没有签出,release
,所以release
设置为某个特定的提交哈希,该怎么办?我们的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
一起使用,即使分支占用其工作树和索引对。