Add --review and --cc flags to repo upload, so you can
assign reviewers when you upload changes.
diff --git a/codereview/__init__.py b/codereview/__init__.py
index 8221688..5883555 100644
--- a/codereview/__init__.py
+++ b/codereview/__init__.py
@@ -1 +1 @@
-__version__ = 'v1.0-99-g9cd3ea2f'
+__version__ = 'v1.0-112-gbcd4db5a'
diff --git a/codereview/upload_bundle_pb2.py b/codereview/upload_bundle_pb2.py
index 7cf2f86..ff91ee1 100644
--- a/codereview/upload_bundle_pb2.py
+++ b/codereview/upload_bundle_pb2.py
@@ -35,23 +35,27 @@
       options=None,
       type=None),
     descriptor.EnumValueDescriptor(
-      name='UNKNOWN_PROJECT', index=5, number=2,
+      name='UNKNOWN_EMAIL', index=5, number=11,
       options=None,
       type=None),
     descriptor.EnumValueDescriptor(
-      name='UNKNOWN_BRANCH', index=6, number=3,
+      name='UNKNOWN_PROJECT', index=6, number=2,
       options=None,
       type=None),
     descriptor.EnumValueDescriptor(
-      name='UNKNOWN_BUNDLE', index=7, number=5,
+      name='UNKNOWN_BRANCH', index=7, number=3,
       options=None,
       type=None),
     descriptor.EnumValueDescriptor(
-      name='NOT_BUNDLE_OWNER', index=8, number=6,
+      name='UNKNOWN_BUNDLE', index=8, number=5,
       options=None,
       type=None),
     descriptor.EnumValueDescriptor(
-      name='BUNDLE_CLOSED', index=9, number=8,
+      name='NOT_BUNDLE_OWNER', index=9, number=6,
+      options=None,
+      type=None),
+    descriptor.EnumValueDescriptor(
+      name='BUNDLE_CLOSED', index=10, number=8,
       options=None,
       type=None),
   ],
@@ -136,6 +140,20 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
+    descriptor.FieldDescriptor(
+      name='reviewers', full_name='codereview.UploadBundleRequest.reviewers', index=6,
+      number=3, type=9, cpp_type=9, label=3,
+      default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    descriptor.FieldDescriptor(
+      name='cc', full_name='codereview.UploadBundleRequest.cc', index=7,
+      number=4, type=9, cpp_type=9, label=3,
+      default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
   ],
   extensions=[
   ],
@@ -165,6 +183,20 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
+    descriptor.FieldDescriptor(
+      name='invalid_reviewers', full_name='codereview.UploadBundleResponse.invalid_reviewers', index=2,
+      number=12, type=9, cpp_type=9, label=3,
+      default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    descriptor.FieldDescriptor(
+      name='invalid_cc', full_name='codereview.UploadBundleResponse.invalid_cc', index=3,
+      number=13, type=9, cpp_type=9, label=3,
+      default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
   ],
   extensions=[
   ],
diff --git a/gerrit_upload.py b/gerrit_upload.py
index d3d4ff3..17112aa 100755
--- a/gerrit_upload.py
+++ b/gerrit_upload.py
@@ -75,6 +75,7 @@
                  dest_branch,
                  src_branch,
                  bases,
+                 people,
                  replace_changes = None,
                  save_cookies=True):
 
@@ -112,6 +113,10 @@
         req = UploadBundleRequest()
         req.dest_project = str(dest_project)
         req.dest_branch = str(dest_branch)
+        for e in people[0]:
+          req.reviewers.append(e)
+        for e in people[1]:
+          req.cc.append(e)
         for c in revlist:
           req.contained_object.append(c)
         if replace_changes:
@@ -158,6 +163,10 @@
           reason = 'invalid change id'
         elif rsp.status_code == UploadBundleResponse.CHANGE_CLOSED:
           reason = 'one or more changes are closed'
+        elif rsp.status_code == UploadBundleResponse.UNKNOWN_EMAIL:
+          emails = [x for x in rsp.invalid_reviewers] + [
+                    x for x in rsp.invalid_cc]
+          reason = 'invalid email addresses: %s' % ", ".join(emails)
         else:
           reason = 'unknown error ' + str(rsp.status_code)
         raise UploadError(reason)
diff --git a/project.py b/project.py
index 3955033..fe3ce34 100644
--- a/project.py
+++ b/project.py
@@ -142,9 +142,10 @@
       R_HEADS + self.name,
       '--')
 
-  def UploadForReview(self):
+  def UploadForReview(self, people):
     self.project.UploadForReview(self.name,
-                                 self.replace_changes)
+                                 self.replace_changes,
+                                 people)
 
   @property
   def tip_url(self):
@@ -456,7 +457,7 @@
         return rb
     return None
 
-  def UploadForReview(self, branch=None, replace_changes=None):
+  def UploadForReview(self, branch=None, replace_changes=None, people=([],[])):
     """Uploads the named branch for code review.
     """
     if branch is None:
@@ -495,6 +496,7 @@
                    dest_branch = dest_branch,
                    src_branch = R_HEADS + branch.name,
                    bases = base_list,
+                   people = people,
                    replace_changes = replace_changes)
     except proto_client.ClientLoginError:
       raise UploadError('Login failure')
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 11f035d..49d0018 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -25,11 +25,17 @@
   print >>sys.stderr, 'error: %s' % msg
   sys.exit(1)
 
+def _SplitEmails(values):
+  result = []
+  for str in values:
+    result.extend([s.strip() for s in str.split(',')])
+  return result
+
 class Upload(InteractiveCommand):
   common = True
   helpSummary = "Upload changes for code review"
   helpUsage="""
-%prog {[<project>]... | --replace <project>}
+%prog [--re --cc] {[<project>]... | --replace <project>}
 """
   helpDescription = """
 The '%prog' command is used to send changes to the Gerrit code
@@ -44,14 +50,25 @@
 by a relative or absolute path to the project's local directory. If
 no projects are specified, '%prog' will search for uploadable
 changes in all projects listed in the manifest.
+
+If the --reviewers or --cc options are passed, those emails are
+added to the respective list of users, and emails are sent to any
+new users.  Users passed to --reviewers must be already registered
+with the code review system, or the upload will fail.
 """
 
   def _Options(self, p):
     p.add_option('--replace',
                  dest='replace', action='store_true',
                  help='Upload replacement patchesets from this branch')
+    p.add_option('--re', '--reviewers',
+                 type='string',  action='append', dest='reviewers',
+                 help='Request reviews from these people.')
+    p.add_option('--cc',
+                 type='string',  action='append', dest='cc',
+                 help='Also send email to these email addresses.')
 
-  def _SingleBranch(self, branch):
+  def _SingleBranch(self, branch, people):
     project = branch.project
     name = branch.name
     date = branch.date
@@ -69,11 +86,11 @@
     sys.stdout.write('(y/n)? ')
     answer = sys.stdin.readline().strip()
     if answer in ('y', 'Y', 'yes', '1', 'true', 't'):
-      self._UploadAndReport([branch])
+      self._UploadAndReport([branch], people)
     else:
       _die("upload aborted by user")
 
-  def _MultipleBranches(self, pending):
+  def _MultipleBranches(self, pending, people):
     projects = {}
     branches = {}
 
@@ -132,7 +149,7 @@
         todo.append(branch)
     if not todo:
       _die("nothing uncommented for upload")
-    self._UploadAndReport(todo)
+    self._UploadAndReport(todo, people)
 
   def _ReplaceBranch(self, project):
     branch = project.CurrentBranch
@@ -176,13 +193,13 @@
       sys.exit(1)
 
     branch.replace_changes = to_replace
-    self._UploadAndReport([branch])
+    self._UploadAndReport([branch], people)
 
-  def _UploadAndReport(self, todo):
+  def _UploadAndReport(self, todo, people):
     have_errors = False
     for branch in todo:
       try:
-        branch.UploadForReview()
+        branch.UploadForReview(people)
         branch.uploaded = True
       except UploadError, e:
         branch.error = e
@@ -216,6 +233,14 @@
   def Execute(self, opt, args):
     project_list = self.GetProjects(args)
     pending = []
+    reviewers = []
+    cc = []
+
+    if opt.reviewers:
+      reviewers = _SplitEmails(opt.reviewers)
+    if opt.cc:
+      cc = _SplitEmails(opt.cc)
+    people = (reviewers,cc)
 
     if opt.replace:
       if len(project_list) != 1:
@@ -233,6 +258,6 @@
     if not pending:
       print >>sys.stdout, "no branches ready for upload"
     elif len(pending) == 1 and len(pending[0][1]) == 1:
-      self._SingleBranch(pending[0][1][0])
+      self._SingleBranch(pending[0][1][0], people)
     else:
-      self._MultipleBranches(pending)
+      self._MultipleBranches(pending, people)