merger: Handle merges with cherry-pick merge-mode

Merges cannot be cherry-picked in git, so if a change is a merge, do a
`git merge` instead of a cherry-pick to match how Gerrit will merge the
change.

Change-Id: I9bc7025d2371913b63f0a6723aff480e7e63d8a3
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
This commit is contained in:
Joshua Watt 2022-04-21 11:19:51 -05:00
parent fd34e0bd1c
commit 968c2ffd1f
4 changed files with 62 additions and 3 deletions

View File

@ -0,0 +1,7 @@
---
features:
- |
The `cherry-pick` merger mode can now handle merges between branches by
performing a `git merge` instead of `git cherry-pick` if the change has
multiple parents. Previously, this would fail because git doesn't allow
a merge to be cherry-picked.

View File

@ -494,7 +494,8 @@ class FakeGerritChange(object):
repo.git.clean('-x', '-f', '-d')
repo.index.merge_tree(parents[1])
r = repo.index.commit(msg)
parent_commits = [repo.commit(p) for p in parents]
r = repo.index.commit(msg, parent_commits=parent_commits)
repo.head.reference = 'master'
repo.head.reset(working_tree=True)

View File

@ -7062,6 +7062,51 @@ class TestSchedulerMerges(ZuulTestCase):
result = self._test_project_merge_mode('cherry-pick')
self.assertEqual(result, expected_messages)
def test_project_merge_mode_cherrypick_branch_merge(self):
"Test that branches can be merged together in cherry-pick mode"
self.create_branch('org/project-merge-branches', 'mp')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project-merge-branches', 'mp'))
self.waitUntilSettled()
path = os.path.join(self.upstream_root, 'org/project-merge-branches')
repo = git.Repo(path)
master_sha = repo.heads.master.commit.hexsha
mp_sha = repo.heads.mp.commit.hexsha
self.executor_server.hold_jobs_in_build = True
M = self.fake_gerrit.addFakeChange(
'org/project-merge-branches', 'master', 'M',
merge_parents=[
master_sha,
mp_sha,
])
M.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(M.addApproval('Approved', 1))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
build = self.builds[-1]
self.assertEqual(build.parameters['zuul']['branch'], 'master')
path = os.path.join(build.jobdir.src_root, 'review.example.com',
"org/project-merge-branches")
repo = git.Repo(path)
repo_messages = [c.message.strip() for c in repo.iter_commits()]
repo_messages.reverse()
correct_messages = [
'initial commit',
'add content from fixture',
'mp commit',
'M-1']
self.assertEqual(repo_messages, correct_messages)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
def test_merge_branch(self):
"Test that the right commits are on alternate branches"
self.create_branch('org/project-merge-branches', 'mp')

View File

@ -555,9 +555,15 @@ class Repo(object):
def cherryPick(self, ref, zuul_event_id=None):
log = get_annotated_logger(self.log, zuul_event_id)
repo = self.createRepoObject(zuul_event_id)
log.debug("Cherry-picking %s", ref)
self.fetch(ref, zuul_event_id=zuul_event_id)
repo.git.cherry_pick("FETCH_HEAD")
if len(repo.commit("FETCH_HEAD").parents) > 1:
args = ["-s", "resolve", "FETCH_HEAD"]
log.debug("Merging %s with args %s instead of cherry-picking",
ref, args)
repo.git.merge(*args)
else:
log.debug("Cherry-picking %s", ref)
repo.git.cherry_pick("FETCH_HEAD")
return repo.head.commit
def merge(self, ref, strategy=None, zuul_event_id=None):