Read about our upcoming Code of Conduct on this issue

Commit 5c6349e2 authored by Georges Racinet's avatar Georges Racinet 🦑
Browse files

Mercurial implementation of the fast-forward merge method

Closes #142

This translates pretty much into `hg phase -p`.

Most safeguards are been done by GitLab before this low-level method is
called, but we still have to add our own: we don't want to publish
a changeset from another branch (nor accept this if the target GitLab
branch doesn't correspond to a Mercurial named branch).

Note for the future: the `with_branch` method will move the Git
branch once the closure is done. That was why we had to avoid sending
Git hooks there and in the other merge method.

branch : heptapod
parent 960b39c9e07c
......@@ -862,10 +862,13 @@ def delete_refs(*ref_names)
def ff_merge(user, source, target_branch, merge_request: nil)
their_commit_id = commit(source)&.id
raise 'Invalid merge source' if their_commit_id.nil?
meth_name = project.vcs_type == 'hg_git' ? :hg_ff_merge : :ff_merge
merge_request&.update(in_progress_merge_commit_sha: their_commit_id)
with_cache_hooks { raw.ff_merge(user, their_commit_id, target_branch) }
with_cache_hooks {
raw.public_send(meth_name, user, their_commit_id, target_branch)
def revert(
......@@ -20,7 +20,7 @@
= form.radio_button :merge_method, :ff, class: "js-merge-method-radio qa-radio-button-merge-ff form-check-input"
= label_tag :project_merge_method_ff, class: 'form-check-label' do
= s_('ProjectSettings|NOT SUPPORTED Fast-forward merge')
= s_('ProjectSettings|Publication without merge (fast-forward)')
= s_('ProjectSettings|This is not supported in this version of Heptapod. If this is selected, expect deep problems with Merge Requests on this project. Please switch to a supported setting immediately.')
= s_('ProjectSettings|No merge commits are created, the "merge" is actually just the publication of the changesets from the source topic. This is possible only if the target branch and the source topic together make a linear subset of the whole history and if the source changesets are on the target Mercurial named branch.')
......@@ -782,6 +782,57 @@ def ff_merge(user, source_sha, target_branch)
def hg_ff_merge(user, source_sha, target_branch)
source_hgsha = hg_sha_map[source_sha]
raise ArgumentError, 'Invalid merge target' unless find_branch(target_branch).target
hg_exe = Gitlab.config.mercurial.bin_path
hg_env = {
# `name` and `email` are as in `committer` (which
# does not have `username`) but the purpose here is
# different: we want to pass them
# along regardless of what should be a commit authorship.
# Of that later information would change for any reason
# these would still be directly linked to authenticated
# user info
'HEPTAPOD_PROJECT_NAMESPACE_FULL_PATH' => @relative_path.rpartition('/')[0],
'HGRCPATH' => Gitlab::Mercurial.hgrc_path
output, status = popen(
[hg_exe, 'log', '-r', source_hgsha, '-T', '{branch}'],
@hgpath, hg_env)
if status != 0
raise MergeError, "Could not log changeset #{source_hgsha}"
hg_branch = output.strip
if target_branch != "branch/" + hg_branch
# TODO how to return the proper error 406 seen from API?
raise MergeError, "Being on branch #{hg_branch}, "\
"changeset #{source_hgsha} cannot be published "\
"within #{target_branch}"
output, status = popen(
[hg_exe, 'phase', '--public', '-r', source_hgsha],
@hgpath, hg_env)
if status != 0
raise MergeError, "Could not publish changeset #{source_hgsha}: #{output}"
end, false, false)
rescue Gitlab::Git::CommitError
# Pull a given revision from URL forcing topic on all new changesets
# Return:
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment