你有many选项;使用哪个取决于您对环境的保证。
你可以使用(帽子尖jthill https://stackoverflow.com/questions/48968509/how-to-promote-ff-branch-to-head/48969434?noredirect=1#comment84957959_48969434):
git checkout release && git merge --ff-only @{1}
(使用您的外壳所需的任何东西来保护支架免于膨胀)。这相当于你的第一个方法,但不需要临时变量,因为@{1}
意思是“之前的值HEAD
结账前”。
但是,您也可以使用以下任一相当棘手的操作:
git push . HEAD:refs/heads/release
or:
git fetch . HEAD:refs/heads/release
正如我们将在下面看到的。这在某些方面更有效。它的主要缺点是几乎没有人groks https://www.merriam-webster.com/dictionary/grok it.
背景
首先我们要注意的是,每个存储库都是独立的,我的意思是每个存储库都有自己的分支名称和自己的分支名称。HEAD
以及索引和工作树。 (后三项——HEAD
、索引和工作树——作为一个单元运行。如果您有 Git 版本 2.5 或更高版本,您可以使用git worktree add
添加独立的工作树,每个工作树都有自己的HEAD
和索引。)
git checkout name-or-ID
假设一切顺利的话:
- Leave
HEAD
指向ID
,通常通过name
(一个“附加的头”),但如果有必要的话可以直接(当你给它一个ID,或者使用--detach
以确保它将 HEAD 与name
你给它)。
- 填写提交中的索引
HEAD
现在的名字。
- 从同一提交填充工作树。
现在,让我们考虑一下什么git merge
当可以快进而不是实际合并时会这样做。出现这种情况时HEAD
命名一个提交,它是目标提交的祖先,然后你告诉git merge
与标识该目标提交的任何内容合并。您传递给的标识符git merge
可以是原始哈希 ID(如您的%HEAD_commit_id%
方法),或分支名称(如您的git merge current-head
方法)。唯一需要的是git merge
:
- 找到目标提交(例如,接收其哈希 ID 或将名称解析为其哈希 ID);
- 计算合并基数
HEAD
和目标提交;和
- 发现这个合并基础与
HEAD
识别。
在这些条件下可以进行快进合并。如果可以进行快进合并,and你没有明确禁止它(git merge --no-ff
) 或隐含地 (git merge -s ours
例如,或使用带注释的标签进行合并的某些情况),git merge
在这种情况下将执行快进操作。您甚至可以添加--ff-only
论据git merge
告诉 Git:如果可能的话,进行快进,如果不可能,则给我一个错误,而不是进行真正的合并。
快进合并操作
现在让我们看看快进合并实际上做了什么。这有多个部分,因为git merge
影响当前(HEAD附加)分支,或者如果HEAD
是分离的,HEAD
本身。请注意,如果中途出现问题,Git 会尝试取消整个操作,这样看起来要么一切都成功,要么快进根本不会开始。 (在某些特别严重的情况下,例如当计算机着火时,如果您的计算机在大火中幸存下来,则混乱的内部状态可能会显现出来。)
Git需要使索引和工作树与合并结果相匹配,这当然是目标提交。因此,实际上,Git 会检查目标提交。然而,HEAD
保持与当前分支的连接(如果已附加)。目标提交的检出会更新索引和工作树。
-
If HEAD
已附加,Git 现在更改分支name到哪个HEAD
附加,以便该名称标识刚刚签出的提交 - 目标提交。也就是说,如果我们有一些东西,我们可能会这样画:
...--o--o <-- branch (HEAD)
\
o--o--o <-- target_commit
\
o--...
我们现在有:
...--o--o
\
o--o--o <-- branch (HEAD), target_commit
\
o--...
当我们在给定的分支上时,这就是正在执行的快进操作。
(If HEAD
是超然的,简单地想象没有名字的相同动作branch
在它前面。)
考虑git fetch
and git push
当你使用git fetch
从另一个 Git 获取新的提交,或者git push
要将您自己的提交提供给另一个 Git,您需要让两个 Git 相互通信并传输任何新的提交。暂时假设这是一个git fetch
手术:
...--o--o--X
becomes:
...--o--o--X--Y--Z
where Y--Z
是与提示提交链接的新提交X
我们分行的。很典型的是,X
有两个名字指向它,例如master
and origin/master
,在start操作的:
...--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
,不同之处在于我们不是让我们的 Git 从他们的 Git 检索提交,而是让我们的提交向他们的 Git 提供提交。但一旦我们这样做了,我们就不想our需要记住的 GittheirGit的主人,我们要theirGit 要记住,如their master
,我们的最终提交。因此,如果they had:
...--o--o--X <-- master
我们给了他们--Y--Z
连接到X
,我们想要them使their master
指向Z
:
...--o--o--X--Y--Z <-- master
这也是一个快进手术。
请注意,如果我们提出请求isn't快进,其他 Git 通常rejects我们的要求:
...--o--o--X--W <-- master
\
Y--Z <-- (we request that they set their master here)
在这种情况下,如果他们搬了名字master
指向Z
,他们会“忘记”承诺W
。那是一个非快进操作,并且是当我们运行时的那种事情git merge
,使我们获得真正的合并。正在接收的 Gitgit push
won't run git merge
, so it requires我们的请求是快进操作——嗯,需要这个,除非我们告诉它force改变。
只要我们不使用--force
或同等的,这种git push
不会进行任何非快进操作的更改。另一个 Git 会简单地说:不,我不会那样做,因为这不是快进。 https://stackoverflow.com/q/4684352/1256452
Note that unlike our fast-forward merge case, their Git—the other Git to which we are git push
ing—doesn't run git checkout
on any updated commit hashes. This is why we must either push to a --bare
repository, which has no work-tree, or push to a branch that the other Git doesn't have checked out.1
这意味着我们可以(ab)使用 fetch 或 push
我们知道:
-
git push
只要分支尚未签出,就会快进,并且
- 我们处于“分离头”模式,所以我们没有分支
release
签出,所以
- 如果我们让 Git 调用我们自己的 Git 会怎么样?如果我们要求他们设置怎么办their
release
某个特定的提交哈希?
我们的 Git 会要求他们的 Git(实际上是我们的 Git,只是戴着不同的帽子)接受任何新的提交——没有任何提交;我们的 Git 拥有我们的 Git 的所有提交,然后更新它的(我们的)release
以快进的方式,因为我们不要求它强制执行任何操作。因此我们可以快进我们自己的release
到由HEAD
如果我们只是说:嘿,其他 Git,设置你的refs/heads/release
到我确定的提交HEAD
!我们通过推动来做到这一点refspec形式:
HEAD:refs/heads/release
现在唯一的办法就是给我们自己打电话。通常我们可能需要一个 URL,但是 Git 允许路径名,包括相对路径名;和相对路径名.
means 当前目录。所以如果我们:
git push . HEAD:refs/heads/release
我们将让我们的 Git 调用自己的 Git,不传输任何内容(没有新的提交),并要求我们的 Git 设置我们的refs/heads/release
以快进的方式,以匹配我们当前的HEAD
。自从我们的release
未检出,当且仅当操作是快进时才允许这样做。
获取技巧完全相同:我们从自己那里获取(不传输任何提交),然后请求我们自己的 Git 设置我们自己的refs/heads/release
到特定的提交哈希。由于没有领先+
强制标志,这也验证了该操作仅快进。因为我们不使用--update-head-ok
,这同样检查git push
确实,当前HEAD
没有命名分支release
。奇怪的是,我们从other这次是 Git(但那是我们自己,所以无论如何它都是相同的哈希 ID)。
1This actually depends on the setting of receive.denyCurrentBranch
. See also receive.denyNonFastForwards
. Newer Gits also support updateInstead
with which we can have the target Git run git checkout
, but only in certain circumstances; this allows non-bare repositories to be used with git push
even with a branch occupying their work-tree-and-index pair.