launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18352
[Merge] lp:~cjwatson/launchpad/git-mp-create into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-mp-create into lp:launchpad with lp:~cjwatson/launchpad/git-mp-basic-model as a prerequisite.
Commit message:
Make it possible to create Git merge proposals, currently only using the webservice.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1445017 in Launchpad itself: "Support for Launchpad Git merge proposals "
https://bugs.launchpad.net/launchpad/+bug/1445017
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-mp-create/+merge/257138
Following on from https://code.launchpad.net/~cjwatson/launchpad/git-mp-basic-model/+merge/257122, this adds initial support for creating Git merge proposals, enough to be usable on the webservice and to bring up internal testing facilities. While it should be self-consistent, actually trying to create these will give you something that the browser code can't render yet; the next couple of branches will improve things there.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-mp-create into lp:launchpad.
=== modified file 'lib/lp/_schema_circular_imports.py'
--- lib/lp/_schema_circular_imports.py 2015-04-21 16:53:45 +0000
+++ lib/lp/_schema_circular_imports.py 2015-04-22 16:18:53 +0000
@@ -563,6 +563,11 @@
# IGitRef
patch_reference_property(IGitRef, 'repository', IGitRepository)
+patch_plain_parameter_type(
+ IGitRef, 'createMergeProposal', 'merge_target', IGitRef)
+patch_plain_parameter_type(
+ IGitRef, 'createMergeProposal', 'merge_prerequisite', IGitRef)
+patch_entry_return_type(IGitRef, 'createMergeProposal', IBranchMergeProposal)
# IGitRepository
patch_collection_property(IGitRepository, 'branches', IGitRef)
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2015-04-21 16:53:45 +0000
+++ lib/lp/code/browser/configure.zcml 2015-04-22 16:18:53 +0000
@@ -811,6 +811,9 @@
path_expression="string:+ref/${name}"
attribute_to_parent="repository"
rootsite="code"/>
+ <browser:navigation
+ module="lp.code.browser.gitref"
+ classes="GitRefNavigation"/>
<browser:pages
for="lp.code.interfaces.gitref.IGitRef"
class="lp.code.browser.gitref.GitRefView"
=== modified file 'lib/lp/code/browser/gitref.py'
--- lib/lp/code/browser/gitref.py 2015-03-19 17:04:22 +0000
+++ lib/lp/code/browser/gitref.py 2015-04-22 16:18:53 +0000
@@ -6,10 +6,33 @@
__metaclass__ = type
__all__ = [
+ 'GitRefNavigation',
'GitRefView',
]
-from lp.services.webapp import LaunchpadView
+from lp.code.interfaces.gitref import IGitRef
+from lp.services.webapp import (
+ LaunchpadView,
+ Navigation,
+ stepthrough,
+ )
+
+
+class GitRefNavigation(Navigation):
+
+ usedfor = IGitRef
+
+ @stepthrough("+merge")
+ def traverse_merge_proposal(self, id):
+ """Traverse to an `IBranchMergeProposal`."""
+ try:
+ id = int(id)
+ except ValueError:
+ # Not a number.
+ return None
+ for proposal in self.context.landing_targets:
+ if proposal.id == id:
+ return proposal
class GitRefView(LaunchpadView):
=== modified file 'lib/lp/code/interfaces/gitnamespace.py'
--- lib/lp/code/interfaces/gitnamespace.py 2015-03-03 14:37:54 +0000
+++ lib/lp/code/interfaces/gitnamespace.py 2015-04-22 16:18:53 +0000
@@ -93,6 +93,9 @@
"True iff this namespace permits automatically setting a default "
"repository on push.")
+ supports_merge_proposals = Attribute(
+ "Does this namespace support merge proposals at all?")
+
def getAllowedInformationTypes(who):
"""Get the information types that a repository in this namespace can
have.
@@ -148,6 +151,14 @@
already exists in the namespace.
"""
+ def areRepositoriesMergeable(other_namespace):
+ """Are repositories from other_namespace mergeable into this one?"""
+
+ collection = Attribute("An `IGitCollection` for this namespace.")
+
+ def assignKarma(person, action_name, date_created=None):
+ """Assign karma to the person on the appropriate target."""
+
class IGitNamespaceSet(Interface):
"""Interface for getting Git repository namespaces."""
=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py 2015-04-22 16:18:53 +0000
+++ lib/lp/code/interfaces/gitref.py 2015-04-22 16:18:53 +0000
@@ -11,23 +11,34 @@
]
from lazr.restful.declarations import (
+ call_with,
export_as_webservice_entry,
+ export_factory_operation,
exported,
- )
-from lazr.restful.fields import ReferenceChoice
+ operation_for_version,
+ operation_parameters,
+ REQUEST_USER,
+ )
+from lazr.restful.fields import (
+ Reference,
+ ReferenceChoice,
+ )
from zope.interface import (
Attribute,
Interface,
)
from zope.schema import (
+ Bool,
Choice,
Datetime,
+ List,
Text,
TextLine,
)
from lp import _
from lp.code.enums import GitObjectType
+from lp.registry.interfaces.person import IPerson
from lp.services.webapp.interfaces import ITableBatchNavigator
@@ -183,6 +194,68 @@
"A collection of the merge proposals that are dependent on this "
"reference.")
+ # XXX cjwatson 2015-04-16: Rename in line with landing_targets above
+ # once we have a better name.
+ def addLandingTarget(registrant, merge_target, merge_prerequisite=None,
+ date_created=None, needs_review=None,
+ description=None, review_requests=None,
+ commit_message=None):
+ """Create a new BranchMergeProposal with this reference as the source.
+
+ Both the target and the prerequisite, if it is there, must be
+ references whose repositories have the same target as the source.
+
+ References in personal repositories cannot specify merge proposals.
+
+ :param registrant: The person who is adding the landing target.
+ :param merge_target: Must be another reference, and different to
+ self.
+ :param merge_prerequisite: Optional, but if it is not None it must
+ be another reference.
+ :param date_created: Used to specify the date_created value of the
+ merge request.
+ :param needs_review: Used to specify the proposal is ready for
+ review right now.
+ :param description: A description of the bugs fixed, features added,
+ or refactorings.
+ :param review_requests: An optional list of (`Person`, review_type).
+ """
+
+ @operation_parameters(
+ # merge_target and merge_prerequisite are actually IGitRef, patched
+ # in _schema_circular_imports.
+ merge_target=Reference(schema=Interface),
+ merge_prerequisite=Reference(schema=Interface),
+ needs_review=Bool(
+ title=_("Needs review"),
+ description=_(
+ "If True, the proposal needs review. Otherwise, it will be "
+ "work in progress.")),
+ initial_comment=Text(
+ title=_("Initial comment"),
+ description=_("Registrant's initial description of proposal.")),
+ commit_message=Text(
+ title=_("Commit message"),
+ description=_("Message to use when committing this merge.")),
+ reviewers=List(value_type=Reference(schema=IPerson)),
+ review_types=List(value_type=TextLine()))
+ @call_with(registrant=REQUEST_USER)
+ # Really IBranchMergeProposal, patched in _schema_circular_imports.py.
+ @export_factory_operation(Interface, [])
+ @operation_for_version("devel")
+ def createMergeProposal(registrant, merge_target, merge_prerequisite=None,
+ needs_review=None, initial_comment=None,
+ commit_message=None, reviewers=None,
+ review_types=None):
+ """Create a new BranchMergeProposal with this reference as the source.
+
+ Both the merge_target and the merge_prerequisite, if it is there,
+ must be references whose repositories have the same target as the
+ source.
+
+ References in personal repositories cannot specify merge proposals.
+ """
+
class IGitRefBatchNavigator(ITableBatchNavigator):
pass
=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py 2015-04-21 23:25:19 +0000
+++ lib/lp/code/interfaces/gitrepository.py 2015-04-22 16:18:53 +0000
@@ -446,6 +446,9 @@
and their subscriptions.
"""
+ def isRepositoryMergeable(other):
+ """Is the other repository mergeable into this one (or vice versa)?"""
+
class IGitRepositoryModerateAttributes(Interface):
"""IGitRepository attributes that can be edited by more than one community.
=== modified file 'lib/lp/code/model/gitnamespace.py'
--- lib/lp/code/model/gitnamespace.py 2015-04-21 17:36:21 +0000
+++ lib/lp/code/model/gitnamespace.py 2015-04-22 16:18:53 +0000
@@ -16,7 +16,10 @@
from zope.component import getUtility
from zope.event import notify
from zope.interface import implements
-from zope.security.proxy import removeSecurityProxy
+from zope.security.proxy import (
+ isinstance as zope_isinstance,
+ removeSecurityProxy,
+ )
from lp.app.enums import (
FREE_INFORMATION_TYPES,
@@ -36,6 +39,7 @@
GitRepositoryCreatorNotOwner,
GitRepositoryExists,
)
+from lp.code.interfaces.gitcollection import IAllGitRepositories
from lp.code.interfaces.gitnamespace import (
IGitNamespace,
IGitNamespacePolicy,
@@ -214,6 +218,7 @@
has_defaults = False
allow_push_to_set_default = False
+ supports_merge_proposals = False
def __init__(self, person):
self.owner = person
@@ -262,6 +267,21 @@
else:
return InformationType.PUBLIC
+ def areRepositoriesMergeable(self, other_namespace):
+ """See `IGitNamespacePolicy`."""
+ return False
+
+ @property
+ def collection(self):
+ """See `IGitNamespacePolicy`."""
+ return getUtility(IAllGitRepositories).ownedBy(
+ self.person).isPersonal()
+
+ def assignKarma(self, person, action_name, date_created=None):
+ """See `IGitNamespacePolicy`."""
+ # Does nothing. No karma for personal repositories.
+ return None
+
class ProjectGitNamespace(_BaseGitNamespace):
"""A namespace for project repositories.
@@ -274,6 +294,7 @@
has_defaults = True
allow_push_to_set_default = True
+ supports_merge_proposals = True
def __init__(self, person, project):
self.owner = person
@@ -325,6 +346,27 @@
return None
return default_type
+ def areRepositoriesMergeable(self, other_namespace):
+ """See `IGitNamespacePolicy`."""
+ # Repositories are mergeable into a project repository if the
+ # project is the same.
+ # XXX cjwatson 2015-04-18: Allow merging from a package repository
+ # if any (active?) series is linked to this project.
+ if zope_isinstance(other_namespace, ProjectGitNamespace):
+ return self.target == other_namespace.target
+ else:
+ return False
+
+ @property
+ def collection(self):
+ """See `IGitNamespacePolicy`."""
+ return getUtility(IAllGitRepositories).inProject(self.project)
+
+ def assignKarma(self, person, action_name, date_created=None):
+ """See `IGitNamespacePolicy`."""
+ return person.assignKarma(
+ action_name, product=self.project, datecreated=date_created)
+
class PackageGitNamespace(_BaseGitNamespace):
"""A namespace for distribution source package repositories.
@@ -337,6 +379,7 @@
has_defaults = True
allow_push_to_set_default = False
+ supports_merge_proposals = True
def __init__(self, person, distro_source_package):
self.owner = person
@@ -376,6 +419,30 @@
"""See `IGitNamespace`."""
return InformationType.PUBLIC
+ def areRepositoriesMergeable(self, other_namespace):
+ """See `IGitNamespacePolicy`."""
+ # Repositories are mergeable into a package repository if the
+ # package is the same.
+ # XXX cjwatson 2015-04-18: Allow merging from a project repository
+ # if any (active?) series links this package to that project.
+ if zope_isinstance(other_namespace, PackageGitNamespace):
+ return self.target == other_namespace.target
+ else:
+ return False
+
+ @property
+ def collection(self):
+ """See `IGitNamespacePolicy`."""
+ return getUtility(IAllGitRepositories).inDistributionSourcePackage(
+ self.distro_source_package)
+
+ def assignKarma(self, person, action_name, date_created=None):
+ """See `IGitNamespacePolicy`."""
+ dsp = self.distro_source_package
+ return person.assignKarma(
+ action_name, distribution=dsp.distribution,
+ sourcepackagename=dsp.sourcepackagename, datecreated=date_created)
+
def __eq__(self, other):
"""See `IGitNamespace`."""
# We may have different DSP objects that are functionally the same.
=== modified file 'lib/lp/code/model/gitref.py'
--- lib/lp/code/model/gitref.py 2015-04-22 16:18:53 +0000
+++ lib/lp/code/model/gitref.py 2015-04-22 16:18:53 +0000
@@ -18,15 +18,32 @@
Store,
Unicode,
)
+from zope.event import notify
from zope.interface import implements
from lp.app.errors import NotFoundError
-from lp.code.enums import GitObjectType
+from lp.code.enums import (
+ BranchMergeProposalStatus,
+ GitObjectType,
+ )
+from lp.code.errors import (
+ BranchMergeProposalExists,
+ InvalidBranchMergeProposal,
+ )
+from lp.code.event.branchmergeproposal import (
+ BranchMergeProposalNeedsReviewEvent,
+ NewBranchMergeProposalEvent,
+ )
+from lp.code.interfaces.branch import WrongNumberOfReviewTypeArguments
from lp.code.interfaces.branchmergeproposal import (
BRANCH_MERGE_PROPOSAL_FINAL_STATES,
)
from lp.code.interfaces.gitref import IGitRef
-from lp.code.model.branchmergeproposal import BranchMergeProposal
+from lp.code.model.branchmergeproposal import (
+ BranchMergeProposal,
+ BranchMergeProposalGetter,
+ )
+from lp.services.database.constants import UTC_NOW
from lp.services.database.enumcol import EnumCol
from lp.services.database.interfaces import IStore
from lp.services.database.stormbase import StormBase
@@ -198,6 +215,103 @@
def commit_message_first_line(self):
return self.commit_message.split("\n", 1)[0]
+ def addLandingTarget(self, registrant, merge_target,
+ merge_prerequisite=None, whiteboard=None,
+ date_created=None, needs_review=None,
+ description=None, review_requests=None,
+ commit_message=None):
+ """See `IGitRef`."""
+ if not self.namespace.supports_merge_proposals:
+ raise InvalidBranchMergeProposal(
+ "%s repositories do not support merge proposals." %
+ self.namespace.name)
+ if self == merge_target:
+ raise InvalidBranchMergeProposal(
+ "Source and target references must be different.")
+ if not merge_target.repository.isRepositoryMergeable(self.repository):
+ raise InvalidBranchMergeProposal(
+ "%s is not mergeable into %s" % (
+ self.identity, merge_target.identity))
+ if merge_prerequisite is not None:
+ if not merge_target.repository.isRepositoryMergeable(
+ merge_prerequisite.repository):
+ raise InvalidBranchMergeProposal(
+ "%s is not mergeable into %s" % (
+ merge_prerequisite.identity, self.identity))
+ if self == merge_prerequisite:
+ raise InvalidBranchMergeProposal(
+ "Source and prerequisite references must be different.")
+ if merge_target == merge_prerequisite:
+ raise InvalidBranchMergeProposal(
+ "Target and prerequisite references must be different.")
+
+ getter = BranchMergeProposalGetter
+ for existing_proposal in getter.activeProposalsForBranches(
+ self, merge_target):
+ raise BranchMergeProposalExists(existing_proposal)
+
+ if date_created is None:
+ date_created = UTC_NOW
+
+ if needs_review:
+ queue_status = BranchMergeProposalStatus.NEEDS_REVIEW
+ date_review_requested = date_created
+ else:
+ queue_status = BranchMergeProposalStatus.WORK_IN_PROGRESS
+ date_review_requested = None
+
+ if review_requests is None:
+ review_requests = []
+
+ # If no reviewer is specified, use the default for the branch.
+ if len(review_requests) == 0:
+ review_requests.append((merge_target.code_reviewer, None))
+
+ kwargs = {}
+ for prefix, obj in (
+ ("source", self),
+ ("target", merge_target),
+ ("prerequisite", merge_prerequisite)):
+ if obj is not None:
+ kwargs["%s_git_repository" % prefix] = obj.repository
+ kwargs["%s_git_path" % prefix] = obj.path
+ kwargs["%s_git_commit_sha1" % prefix] = obj.commit_sha1
+
+ bmp = BranchMergeProposal(
+ registrant=registrant, whiteboard=whiteboard,
+ date_created=date_created,
+ date_review_requested=date_review_requested,
+ queue_status=queue_status, commit_message=commit_message,
+ description=description, **kwargs)
+
+ for reviewer, review_type in review_requests:
+ bmp.nominateReviewer(
+ reviewer, registrant, review_type, _notify_listeners=False)
+
+ notify(NewBranchMergeProposalEvent(bmp))
+ if needs_review:
+ notify(BranchMergeProposalNeedsReviewEvent(bmp))
+
+ return bmp
+
+ def createMergeProposal(self, registrant, merge_target,
+ merge_prerequisite=None, needs_review=True,
+ initial_comment=None, commit_message=None,
+ reviewers=None, review_types=None):
+ """See `IGitRef`."""
+ if reviewers is None:
+ reviewers = []
+ if review_types is None:
+ review_types = []
+ if len(reviewers) != len(review_types):
+ raise WrongNumberOfReviewTypeArguments(
+ 'reviewers and review_types must be equal length.')
+ review_requests = zip(reviewers, review_types)
+ return self.addLandingTarget(
+ registrant, merge_target, merge_prerequisite,
+ needs_review=needs_review, description=initial_comment,
+ commit_message=commit_message, review_requests=review_requests)
+
class GitRefFrozen(GitRefMixin):
"""A frozen Git reference.
=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py 2015-04-21 17:36:21 +0000
+++ lib/lp/code/model/gitrepository.py 2015-04-22 16:18:53 +0000
@@ -737,6 +737,10 @@
recipients.add(subscription.person, subscription, rationale)
return recipients
+ def isRepositoryMergeable(self, other):
+ """See `IGitRepository`."""
+ return self.namespace.areRepositoriesMergeable(other.namespace)
+
def destroySelf(self):
raise NotImplementedError
=== modified file 'lib/lp/code/subscribers/karma.py'
--- lib/lp/code/subscribers/karma.py 2015-04-21 16:53:45 +0000
+++ lib/lp/code/subscribers/karma.py 2015-04-22 16:18:53 +0000
@@ -1,4 +1,4 @@
-# Copyright 2011 Canonical Ltd. This software is licensed under the
+# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Assign karma for code domain activity."""
@@ -17,15 +17,21 @@
@block_implicit_flushes
def branch_merge_proposed(proposal, event):
"""Assign karma to the user who proposed the merge."""
- proposal.source_branch.target.assignKarma(
- proposal.registrant, 'branchmergeproposed')
+ if proposal.source_git_repository is not None:
+ target = proposal.source_git_repository.namespace
+ else:
+ target = proposal.source_branch.target
+ target.assignKarma(proposal.registrant, 'branchmergeproposed')
@block_implicit_flushes
def code_review_comment_added(code_review_comment, event):
"""Assign karma to the user who commented on the review."""
proposal = code_review_comment.branch_merge_proposal
- target = proposal.source_branch.target
+ if proposal.source_git_repository is not None:
+ target = proposal.source_git_repository.namespace
+ else:
+ target = proposal.source_branch.target
# If the user is commenting on their own proposal, then they don't
# count as a reviewer for that proposal.
user = code_review_comment.message.owner
@@ -39,7 +45,10 @@
@block_implicit_flushes
def branch_merge_status_changed(proposal, event):
"""Assign karma to the user who approved the merge."""
- target = proposal.source_branch.target
+ if proposal.source_git_repository is not None:
+ target = proposal.source_git_repository.namespace
+ else:
+ target = proposal.source_branch.target
user = IPerson(event.user)
in_progress_states = (
=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
--- lib/lp/code/templates/branchmergeproposal-index.pt 2014-05-21 01:16:13 +0000
+++ lib/lp/code/templates/branchmergeproposal-index.pt 2015-04-22 16:18:53 +0000
@@ -158,16 +158,18 @@
<div id="source-revisions"
tal:condition="not: context/queue_status/enumvalue:MERGED">
- <tal:history-available condition="context/source_branch/revision_count"
- define="branch context/source_branch;
- revisions view/unlanded_revisions">
- <h2>Unmerged revisions</h2>
- <metal:landing-target use-macro="branch/@@+macros/branch-revisions"/>
- </tal:history-available>
- <tal:remote-branch condition="context/source_branch/branch_type/enumvalue:REMOTE">
- <h2>Unmerged revisions</h2>
- <p>Recent revisions are not available due to the source branch being remote.</p>
- </tal:remote-branch>
+ <tal:bzr-revisions condition="context/source_branch">
+ <tal:history-available condition="context/source_branch/revision_count"
+ define="branch context/source_branch;
+ revisions view/unlanded_revisions">
+ <h2>Unmerged revisions</h2>
+ <metal:landing-target use-macro="branch/@@+macros/branch-revisions"/>
+ </tal:history-available>
+ <tal:remote-branch condition="context/source_branch/branch_type/enumvalue:REMOTE">
+ <h2>Unmerged revisions</h2>
+ <p>Recent revisions are not available due to the source branch being remote.</p>
+ </tal:remote-branch>
+ </tal:bzr-revisions>
</div>
</div>
=== modified file 'lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt'
--- lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt 2015-04-21 16:53:45 +0000
+++ lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt 2015-04-22 16:18:53 +0000
@@ -121,7 +121,11 @@
<div tal:replace="structure context/@@++diff-stats" />
</td>
</tr>
- <tr id="summary-row-merge-instruction">
+ <tal:comment condition="nothing">
+ <!-- XXX cjwatson 2015-04-18: Add merge instructions for Git. -->
+ </tal:comment>
+ <tr id="summary-row-merge-instruction"
+ tal:condition="context/source_branch">
<th>To merge this branch:</th>
<td>bzr merge <span class="branch-url" tal:content="context/source_branch/bzr_identity" /></td>
</tr>
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py 2015-04-22 16:18:53 +0000
+++ lib/lp/security.py 2015-04-22 16:18:53 +0000
@@ -2266,6 +2266,15 @@
super(ViewGitRef, self).__init__(obj, obj.repository)
+class EditGitRef(DelegatedAuthorization):
+ """Anyone who can edit a Git repository can edit references within it."""
+ permission = 'launchpad.Edit'
+ usedfor = IGitRef
+
+ def __init__(self, obj):
+ super(EditGitRef, self).__init__(obj, obj.repository)
+
+
class AdminDistroSeriesTranslations(AuthorizationBase):
permission = 'launchpad.TranslationsAdmin'
usedfor = IDistroSeries
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2015-04-21 16:53:45 +0000
+++ lib/lp/testing/factory.py 2015-04-22 16:18:53 +0000
@@ -1477,6 +1477,68 @@
return proposal
+ def makeBranchMergeProposalForGit(self, target_ref=None, registrant=None,
+ set_state=None, prerequisite_ref=None,
+ target=_DEFAULT, initial_comment=None,
+ source_ref=None, date_created=None,
+ description=None, reviewer=None,
+ merged_revision_id=None):
+ """Create a proposal to merge based on anonymous branches."""
+ if target is not _DEFAULT:
+ pass
+ elif target_ref is not None:
+ target = target_ref.target
+ elif source_ref is not None:
+ target = source_ref.target
+ elif prerequisite_ref is not None:
+ target = prerequisite_ref.target
+ else:
+ # Create a reference for a repository on the target, and use
+ # that target.
+ [target_ref] = self.makeGitRefs(target=target)
+ target = target_ref.target
+
+ # Fall back to initial_comment for description.
+ if description is None:
+ description = initial_comment
+
+ if target_ref is None:
+ [target_ref] = self.makeGitRefs(target=target)
+ if source_ref is None:
+ [source_ref] = self.makeGitRefs(target=target)
+ if registrant is None:
+ registrant = self.makePerson()
+ review_requests = []
+ if reviewer is not None:
+ review_requests.append((reviewer, None))
+ proposal = source_ref.addLandingTarget(
+ registrant, target_ref, review_requests=review_requests,
+ merge_prerequisite=prerequisite_ref, description=description,
+ date_created=date_created)
+
+ unsafe_proposal = removeSecurityProxy(proposal)
+ unsafe_proposal.merged_revision_id = merged_revision_id
+ if (set_state is None or
+ set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS):
+ # The initial state is work in progress, so do nothing.
+ pass
+ elif set_state == BranchMergeProposalStatus.NEEDS_REVIEW:
+ unsafe_proposal.requestReview()
+ elif set_state == BranchMergeProposalStatus.CODE_APPROVED:
+ unsafe_proposal.approveBranch(
+ proposal.merge_target.owner, 'some_revision')
+ elif set_state == BranchMergeProposalStatus.REJECTED:
+ unsafe_proposal.rejectBranch(
+ proposal.merge_target.owner, 'some_revision')
+ elif set_state == BranchMergeProposalStatus.MERGED:
+ unsafe_proposal.markAsMerged()
+ elif set_state == BranchMergeProposalStatus.SUPERSEDED:
+ unsafe_proposal.resubmit(proposal.registrant)
+ else:
+ raise AssertionError('Unknown status: %s' % set_state)
+
+ return proposal
+
def makeBranchSubscription(self, branch=None, person=None,
subscribed_by=None):
"""Create a BranchSubscription."""
@@ -1673,10 +1735,10 @@
BranchSubscriptionNotificationLevel.NOEMAIL, None,
CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
- def makeGitRefs(self, repository=None, paths=None):
+ def makeGitRefs(self, repository=None, paths=None, **repository_kwargs):
"""Create and return a list of new, arbitrary GitRefs."""
if repository is None:
- repository = self.makeGitRepository()
+ repository = self.makeGitRepository(**repository_kwargs)
if paths is None:
paths = [self.getUniqueString('refs/heads/path').decode('utf-8')]
refs_info = {
Follow ups