Send reviews to a different branch from fetch

This adds the ability to have reviews pushed to a different branch
from the one on which changes are based. This is useful for "gateway"
systems without smartsync.

Change-Id: I3a8a0fabcaf6055e62d3fb55f89c944e2f81569f
This commit is contained in:
Bryan Jacobs 2013-05-06 13:36:24 -04:00
parent 59bbb580e3
commit f609f91b72
4 changed files with 50 additions and 20 deletions

View File

@ -37,24 +37,26 @@ following DTD:
<!ATTLIST remote review CDATA #IMPLIED> <!ATTLIST remote review CDATA #IMPLIED>
<!ELEMENT default (EMPTY)> <!ELEMENT default (EMPTY)>
<!ATTLIST default remote IDREF #IMPLIED> <!ATTLIST default remote IDREF #IMPLIED>
<!ATTLIST default revision CDATA #IMPLIED> <!ATTLIST default revision CDATA #IMPLIED>
<!ATTLIST default sync-j CDATA #IMPLIED> <!ATTLIST default dest-branch CDATA #IMPLIED>
<!ATTLIST default sync-c CDATA #IMPLIED> <!ATTLIST default sync-j CDATA #IMPLIED>
<!ATTLIST default sync-s CDATA #IMPLIED> <!ATTLIST default sync-c CDATA #IMPLIED>
<!ATTLIST default sync-s CDATA #IMPLIED>
<!ELEMENT manifest-server (EMPTY)> <!ELEMENT manifest-server (EMPTY)>
<!ATTLIST url CDATA #REQUIRED> <!ATTLIST url CDATA #REQUIRED>
<!ELEMENT project (annotation?, <!ELEMENT project (annotation?,
project*)> project*)>
<!ATTLIST project name CDATA #REQUIRED> <!ATTLIST project name CDATA #REQUIRED>
<!ATTLIST project path CDATA #IMPLIED> <!ATTLIST project path CDATA #IMPLIED>
<!ATTLIST project remote IDREF #IMPLIED> <!ATTLIST project remote IDREF #IMPLIED>
<!ATTLIST project revision CDATA #IMPLIED> <!ATTLIST project revision CDATA #IMPLIED>
<!ATTLIST project groups CDATA #IMPLIED> <!ATTLIST project dest-branch CDATA #IMPLIED>
<!ATTLIST project sync-c CDATA #IMPLIED> <!ATTLIST project groups CDATA #IMPLIED>
<!ATTLIST project sync-s CDATA #IMPLIED> <!ATTLIST project sync-c CDATA #IMPLIED>
<!ATTLIST project sync-s CDATA #IMPLIED>
<!ATTLIST project upstream CDATA #IMPLIED> <!ATTLIST project upstream CDATA #IMPLIED>
<!ATTLIST project clone-depth CDATA #IMPLIED> <!ATTLIST project clone-depth CDATA #IMPLIED>
<!ATTLIST project force-path CDATA #IMPLIED> <!ATTLIST project force-path CDATA #IMPLIED>
@ -125,6 +127,11 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or
`refs/heads/master`). Project elements lacking their own `refs/heads/master`). Project elements lacking their own
revision attribute will use this revision. revision attribute will use this revision.
Attribute `dest-branch`: Name of a Git branch (e.g. `master`).
Project elements not setting their own `dest-branch` will inherit
this value. If this value is not set, projects will use `revision`
by default instead.
Attribute `sync_j`: Number of parallel jobs to use when synching. Attribute `sync_j`: Number of parallel jobs to use when synching.
Attribute `sync_c`: Set to true to only sync the given Git Attribute `sync_c`: Set to true to only sync the given Git
@ -203,6 +210,11 @@ Tags and/or explicit SHA-1s should work in theory, but have not
been extensively tested. If not supplied the revision given by been extensively tested. If not supplied the revision given by
the default element is used. the default element is used.
Attribute `dest-branch`: Name of a Git branch (e.g. `master`).
When using `repo upload`, changes will be submitted for code
review on this branch. If unspecified both here and in the
default element, `revision` is used instead.
Attribute `groups`: List of groups to which this project belongs, Attribute `groups`: List of groups to which this project belongs,
whitespace or comma separated. All projects belong to the group whitespace or comma separated. All projects belong to the group
"all", and each project automatically belongs to a group of "all", and each project automatically belongs to a group of

View File

@ -555,6 +555,8 @@ class XmlManifest(object):
if d.revisionExpr == '': if d.revisionExpr == '':
d.revisionExpr = None d.revisionExpr = None
d.destBranchExpr = node.getAttribute('dest-branch') or None
sync_j = node.getAttribute('sync-j') sync_j = node.getAttribute('sync-j')
if sync_j == '' or sync_j is None: if sync_j == '' or sync_j is None:
d.sync_j = 1 d.sync_j = 1
@ -676,6 +678,8 @@ class XmlManifest(object):
raise ManifestParseError('invalid clone-depth %s in %s' % raise ManifestParseError('invalid clone-depth %s in %s' %
(clone_depth, self.manifestFile)) (clone_depth, self.manifestFile))
dest_branch = node.getAttribute('dest-branch') or self._default.destBranchExpr
upstream = node.getAttribute('upstream') upstream = node.getAttribute('upstream')
groups = '' groups = ''
@ -709,7 +713,8 @@ class XmlManifest(object):
sync_s = sync_s, sync_s = sync_s,
clone_depth = clone_depth, clone_depth = clone_depth,
upstream = upstream, upstream = upstream,
parent = parent) parent = parent,
dest_branch = dest_branch)
for n in node.childNodes: for n in node.childNodes:
if n.nodeName == 'copyfile': if n.nodeName == 'copyfile':

View File

@ -157,11 +157,12 @@ class ReviewableBranch(object):
R_HEADS + self.name, R_HEADS + self.name,
'--') '--')
def UploadForReview(self, people, auto_topic=False, draft=False): def UploadForReview(self, people, auto_topic=False, draft=False, dest_branch=None):
self.project.UploadForReview(self.name, self.project.UploadForReview(self.name,
people, people,
auto_topic=auto_topic, auto_topic=auto_topic,
draft=draft) draft=draft,
dest_branch=dest_branch)
def GetPublishedRefs(self): def GetPublishedRefs(self):
refs = {} refs = {}
@ -497,7 +498,8 @@ class Project(object):
clone_depth = None, clone_depth = None,
upstream = None, upstream = None,
parent = None, parent = None,
is_derived = False): is_derived = False,
dest_branch = None):
"""Init a Project object. """Init a Project object.
Args: Args:
@ -517,6 +519,7 @@ class Project(object):
parent: The parent Project object. parent: The parent Project object.
is_derived: False if the project was explicitly defined in the manifest; is_derived: False if the project was explicitly defined in the manifest;
True if the project is a discovered submodule. True if the project is a discovered submodule.
dest_branch: The branch to which to push changes for review by default.
""" """
self.manifest = manifest self.manifest = manifest
self.name = name self.name = name
@ -559,6 +562,7 @@ class Project(object):
self.work_git = None self.work_git = None
self.bare_git = self._GitGetByExec(self, bare=True) self.bare_git = self._GitGetByExec(self, bare=True)
self.bare_ref = GitRefs(gitdir) self.bare_ref = GitRefs(gitdir)
self.dest_branch = dest_branch
# This will be filled in if a project is later identified to be the # This will be filled in if a project is later identified to be the
# project containing repo hooks. # project containing repo hooks.
@ -908,7 +912,8 @@ class Project(object):
def UploadForReview(self, branch=None, def UploadForReview(self, branch=None,
people=([],[]), people=([],[]),
auto_topic=False, auto_topic=False,
draft=False): draft=False,
dest_branch=None):
"""Uploads the named branch for code review. """Uploads the named branch for code review.
""" """
if branch is None: if branch is None:
@ -922,7 +927,10 @@ class Project(object):
if not branch.remote.review: if not branch.remote.review:
raise GitError('remote %s has no review url' % branch.remote.name) raise GitError('remote %s has no review url' % branch.remote.name)
dest_branch = branch.merge if dest_branch is None:
dest_branch = self.dest_branch
if dest_branch is None:
dest_branch = branch.merge
if not dest_branch.startswith(R_HEADS): if not dest_branch.startswith(R_HEADS):
dest_branch = R_HEADS + dest_branch dest_branch = R_HEADS + dest_branch

View File

@ -146,6 +146,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
p.add_option('-d', '--draft', p.add_option('-d', '--draft',
action='store_true', dest='draft', default=False, action='store_true', dest='draft', default=False,
help='If specified, upload as a draft.') help='If specified, upload as a draft.')
p.add_option('-D', '--destination', '--dest',
type='string', action='store', dest='dest_branch',
metavar='BRANCH',
help='Submit for review on this target branch.')
# Options relating to upload hook. Note that verify and no-verify are NOT # Options relating to upload hook. Note that verify and no-verify are NOT
# opposites of each other, which is why they store to different locations. # opposites of each other, which is why they store to different locations.
@ -185,7 +189,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
date = branch.date date = branch.date
commit_list = branch.commits commit_list = branch.commits
print('Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr)) destination = project.dest_branch or project.revisionExpr
print('Upload project %s/ to remote branch %s:' % (project.relpath, destination))
print(' branch %s (%2d commit%s, %s):' % ( print(' branch %s (%2d commit%s, %s):' % (
name, name,
len(commit_list), len(commit_list),
@ -336,7 +341,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
key = 'review.%s.uploadtopic' % branch.project.remote.review key = 'review.%s.uploadtopic' % branch.project.remote.review
opt.auto_topic = branch.project.config.GetBoolean(key) opt.auto_topic = branch.project.config.GetBoolean(key)
branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft, dest_branch=opt.dest_branch)
branch.uploaded = True branch.uploaded = True
except UploadError as e: except UploadError as e:
branch.error = e branch.error = e