Refactor git cl RietveldUpload and GerritUpload.

No functionality changes.

R=machenbach@chromium.org,andybons@chromium.org
BUG=579160,597638

Review URL: https://codereview.chromium.org/1877893002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@299844 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_cl.py b/git_cl.py
index 4590e21..6c78d99 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -1426,6 +1426,10 @@
     failed."""
     raise NotImplementedError()
 
+  def CMDUploadChange(self, options, args, change):
+    """Uploads a change to codereview."""
+    raise NotImplementedError()
+
 
 class _RietveldChangelistImpl(_ChangelistCodereviewBase):
   def __init__(self, changelist, auth_config=None, rietveld_server=None):
@@ -1438,9 +1442,6 @@
     self._props = None
     self._rpc_server = None
 
-  def GetAuthConfig(self):
-    return self._auth_config
-
   def GetCodereviewServer(self):
     if not self._rietveld_server:
       # If we're on a branch then get the server potentially associated
@@ -1687,6 +1688,158 @@
           patch_url=gclient_utils.UpgradeToHttps(parsed_url.geturl()))
     return None
 
+  def CMDUploadChange(self, options, args, change):
+    """Upload the patch to Rietveld."""
+    upload_args = ['--assume_yes']  # Don't ask about untracked files.
+    upload_args.extend(['--server', self.GetCodereviewServer()])
+    # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl.
+    upload_args.extend(auth.auth_config_to_command_options(self._auth_config))
+    if options.emulate_svn_auto_props:
+      upload_args.append('--emulate_svn_auto_props')
+
+    change_desc = None
+
+    if options.email is not None:
+      upload_args.extend(['--email', options.email])
+
+    if self.GetIssue():
+      if options.title:
+        upload_args.extend(['--title', options.title])
+      if options.message:
+        upload_args.extend(['--message', options.message])
+      upload_args.extend(['--issue', str(self.GetIssue())])
+      print ('This branch is associated with issue %s. '
+             'Adding patch to that issue.' % self.GetIssue())
+    else:
+      if options.title:
+        upload_args.extend(['--title', options.title])
+      message = (options.title or options.message or
+                 CreateDescriptionFromLog(args))
+      change_desc = ChangeDescription(message)
+      if options.reviewers or options.tbr_owners:
+        change_desc.update_reviewers(options.reviewers,
+                                     options.tbr_owners,
+                                     change)
+      if not options.force:
+        change_desc.prompt()
+
+      if not change_desc.description:
+        print "Description is empty; aborting."
+        return 1
+
+      upload_args.extend(['--message', change_desc.description])
+      if change_desc.get_reviewers():
+        upload_args.append('--reviewers=%s' % ','.join(
+            change_desc.get_reviewers()))
+      if options.send_mail:
+        if not change_desc.get_reviewers():
+          DieWithError("Must specify reviewers to send email.")
+        upload_args.append('--send_mail')
+
+      # We check this before applying rietveld.private assuming that in
+      # rietveld.cc only addresses which we can send private CLs to are listed
+      # if rietveld.private is set, and so we should ignore rietveld.cc only
+      # when --private is specified explicitly on the command line.
+      if options.private:
+        logging.warn('rietveld.cc is ignored since private flag is specified.  '
+                     'You need to review and add them manually if necessary.')
+        cc = self.GetCCListWithoutDefault()
+      else:
+        cc = self.GetCCList()
+      cc = ','.join(filter(None, (cc, ','.join(options.cc))))
+      if cc:
+        upload_args.extend(['--cc', cc])
+
+    if options.private or settings.GetDefaultPrivateFlag() == "True":
+      upload_args.append('--private')
+
+    upload_args.extend(['--git_similarity', str(options.similarity)])
+    if not options.find_copies:
+      upload_args.extend(['--git_no_find_copies'])
+
+    # Include the upstream repo's URL in the change -- this is useful for
+    # projects that have their source spread across multiple repos.
+    remote_url = self.GetGitBaseUrlFromConfig()
+    if not remote_url:
+      if settings.GetIsGitSvn():
+        remote_url = self.GetGitSvnRemoteUrl()
+      else:
+        if self.GetRemoteUrl() and '/' in self.GetUpstreamBranch():
+          remote_url = '%s@%s' % (self.GetRemoteUrl(),
+                                  self.GetUpstreamBranch().split('/')[-1])
+    if remote_url:
+      upload_args.extend(['--base_url', remote_url])
+      remote, remote_branch = self.GetRemoteBranch()
+      target_ref = GetTargetRef(remote, remote_branch, options.target_branch,
+                                settings.GetPendingRefPrefix())
+      if target_ref:
+        upload_args.extend(['--target_ref', target_ref])
+
+      # Look for dependent patchsets. See crbug.com/480453 for more details.
+      remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
+      upstream_branch = ShortBranchName(upstream_branch)
+      if remote is '.':
+        # A local branch is being tracked.
+        local_branch = ShortBranchName(upstream_branch)
+        if settings.GetIsSkipDependencyUpload(local_branch):
+          print
+          print ('Skipping dependency patchset upload because git config '
+                 'branch.%s.skip-deps-uploads is set to True.' % local_branch)
+          print
+        else:
+          auth_config = auth.extract_auth_config_from_options(options)
+          branch_cl = Changelist(branchref=local_branch,
+                                 auth_config=auth_config)
+          branch_cl_issue_url = branch_cl.GetIssueURL()
+          branch_cl_issue = branch_cl.GetIssue()
+          branch_cl_patchset = branch_cl.GetPatchset()
+          if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset:
+            upload_args.extend(
+                ['--depends_on_patchset', '%s:%s' % (
+                     branch_cl_issue, branch_cl_patchset)])
+            print (
+                '\n'
+                'The current branch (%s) is tracking a local branch (%s) with '
+                'an associated CL.\n'
+                'Adding %s/#ps%s as a dependency patchset.\n'
+                '\n' % (self.GetBranch(), local_branch, branch_cl_issue_url,
+                        branch_cl_patchset))
+
+    project = settings.GetProject()
+    if project:
+      upload_args.extend(['--project', project])
+
+    if options.cq_dry_run:
+      upload_args.extend(['--cq_dry_run'])
+
+    try:
+      upload_args = ['upload'] + upload_args + args
+      logging.info('upload.RealMain(%s)', upload_args)
+      issue, patchset = upload.RealMain(upload_args)
+      issue = int(issue)
+      patchset = int(patchset)
+    except KeyboardInterrupt:
+      sys.exit(1)
+    except:
+      # If we got an exception after the user typed a description for their
+      # change, back up the description before re-raising.
+      if change_desc:
+        backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE)
+        print('\nGot exception while uploading -- saving description to %s\n' %
+              backup_path)
+        backup_file = open(backup_path, 'w')
+        backup_file.write(change_desc.description)
+        backup_file.close()
+      raise
+
+    if not self.GetIssue():
+      self.SetIssue(issue)
+    self.SetPatchset(patchset)
+
+    if options.use_commit_queue:
+      self.SetFlag('commit', '1')
+    return 0
+
 
 class _GerritChangelistImpl(_ChangelistCodereviewBase):
   def __init__(self, changelist, auth_config=None):
@@ -1826,9 +1979,11 @@
     gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(),
                              wait_for_merge=wait_for_merge)
 
-  def _GetChangeDetail(self, options):
-    return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(),
-                                       options)
+  def _GetChangeDetail(self, options=None, issue=None):
+    options = options or []
+    issue = issue or self.GetIssue()
+    assert issue, 'issue required to query Gerrit'
+    return gerrit_util.GetChangeDetail(self._GetGerritHost(), options, issue)
 
   def CMDLand(self, force, bypass_hooks, verbose):
     if git_common.is_dirty_git_tree('land'):
@@ -1922,6 +2077,206 @@
           hostname=parsed_url.netloc)
     return None
 
+  def CMDUploadChange(self, options, args, change):
+    """Upload the current branch to Gerrit."""
+    # We assume the remote called "origin" is the one we want.
+    # It is probably not worthwhile to support different workflows.
+    gerrit_remote = 'origin'
+
+    remote, remote_branch = self.GetRemoteBranch()
+    branch = GetTargetRef(remote, remote_branch, options.target_branch,
+                          pending_prefix='')
+
+    if options.title:
+      # TODO(tandrii): it's now supported by Gerrit, implement!
+      print "\nPatch titles (-t) are not supported in Gerrit. Aborting..."
+      return 1
+
+    if options.squash:
+      if not self.GetIssue():
+        # TODO(tandrii): deperecate this after 2016Q2.  Backwards compatibility
+        # with shadow branch, which used to contain change-id for a given
+        # branch, using which we can fetch actual issue number and set it as the
+        # property of the branch, which is the new way.
+        message = RunGitSilent([
+            'show', '--format=%B', '-s',
+            'refs/heads/git_cl_uploads/%s' % self.GetBranch()])
+        if message:
+          change_ids = git_footers.get_footer_change_id(message.strip())
+          if change_ids and len(change_ids) == 1:
+            details = self._GetChangeDetail(issue=change_ids[0])
+            if details:
+              print('WARNING: found old upload in branch git_cl_uploads/%s '
+                    'corresponding to issue %s' %
+                    (self.GetBranch(), details['_number']))
+              self.SetIssue(details['_number'])
+          if not self.GetIssue():
+            DieWithError(
+                '\n'  # For readability of the blob below.
+                'Found old upload in branch git_cl_uploads/%s, '
+                'but failed to find corresponding Gerrit issue.\n'
+                'If you know the issue number, set it manually first:\n'
+                '    git cl issue 123456\n'
+                'If you intended to upload this CL as new issue, '
+                'just delete or rename the old upload branch:\n'
+                '    git rename-branch git_cl_uploads/%s old_upload-%s\n'
+                'After that, please run git cl upload again.' %
+                tuple([self.GetBranch()] * 3))
+        # End of backwards compatability.
+
+      if self.GetIssue():
+        # Try to get the message from a previous upload.
+        message = self.GetDescription()
+        if not message:
+          DieWithError(
+              'failed to fetch description from current Gerrit issue %d\n'
+              '%s' % (self.GetIssue(), self.GetIssueURL()))
+        change_id = self._GetChangeDetail()['change_id']
+        while True:
+          footer_change_ids = git_footers.get_footer_change_id(message)
+          if footer_change_ids == [change_id]:
+            break
+          if not footer_change_ids:
+            message = git_footers.add_footer_change_id(message, change_id)
+            print('WARNING: appended missing Change-Id to issue description')
+            continue
+          # There is already a valid footer but with different or several ids.
+          # Doing this automatically is non-trivial as we don't want to lose
+          # existing other footers, yet we want to append just 1 desired
+          # Change-Id. Thus, just create a new footer, but let user verify the
+          # new description.
+          message = '%s\n\nChange-Id: %s' % (message, change_id)
+          print(
+              'WARNING: issue %s has Change-Id footer(s):\n'
+              '  %s\n'
+              'but issue has Change-Id %s, according to Gerrit.\n'
+              'Please, check the proposed correction to the description, '
+              'and edit it if necessary but keep the "Change-Id: %s" footer\n'
+              % (self.GetIssue(), '\n  '.join(footer_change_ids), change_id,
+                 change_id))
+          ask_for_data('Press enter to edit now, Ctrl+C to abort')
+          if not options.force:
+            change_desc = ChangeDescription(message)
+            change_desc.prompt()
+            message = change_desc.description
+            if not message:
+              DieWithError("Description is empty. Aborting...")
+          # Continue the while loop.
+        # Sanity check of this code - we should end up with proper message
+        # footer.
+        assert [change_id] == git_footers.get_footer_change_id(message)
+        change_desc = ChangeDescription(message)
+      else:
+        change_desc = ChangeDescription(
+            options.message or CreateDescriptionFromLog(args))
+        if not options.force:
+          change_desc.prompt()
+        if not change_desc.description:
+          DieWithError("Description is empty. Aborting...")
+        message = change_desc.description
+        change_ids = git_footers.get_footer_change_id(message)
+        if len(change_ids) > 1:
+          DieWithError('too many Change-Id footers, at most 1 allowed.')
+        if not change_ids:
+          # Generate the Change-Id automatically.
+          message = git_footers.add_footer_change_id(
+              message, GenerateGerritChangeId(message))
+          change_desc.set_description(message)
+          change_ids = git_footers.get_footer_change_id(message)
+          assert len(change_ids) == 1
+        change_id = change_ids[0]
+
+      remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
+      if remote is '.':
+        # If our upstream branch is local, we base our squashed commit on its
+        # squashed version.
+        upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch)
+        # Check the squashed hash of the parent.
+        parent = RunGit(['config',
+                         'branch.%s.gerritsquashhash' % upstream_branch_name],
+                        error_ok=True).strip()
+        # Verify that the upstream branch has been uploaded too, otherwise
+        # Gerrit will create additional CLs when uploading.
+        if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) !=
+                          RunGitSilent(['rev-parse', parent + ':'])):
+          # TODO(tandrii): remove "old depot_tools" part on April 12, 2016.
+          DieWithError(
+              'Upload upstream branch %s first.\n'
+              'Note: maybe you\'ve uploaded it with --no-squash or with an old '
+              'version of depot_tools. If so, then re-upload it with:\n'
+              '    git cl upload --squash\n' % upstream_branch_name)
+      else:
+        parent = self.GetCommonAncestorWithUpstream()
+
+      tree = RunGit(['rev-parse', 'HEAD:']).strip()
+      ref_to_push = RunGit(['commit-tree', tree, '-p', parent,
+                            '-m', message]).strip()
+    else:
+      change_desc = ChangeDescription(
+          options.message or CreateDescriptionFromLog(args))
+      if not change_desc.description:
+        DieWithError("Description is empty. Aborting...")
+
+      if not git_footers.get_footer_change_id(change_desc.description):
+        DownloadGerritHook(False)
+        change_desc.set_description(AddChangeIdToCommitMessage(options, args))
+      ref_to_push = 'HEAD'
+      parent = '%s/%s' % (gerrit_remote, branch)
+      change_id = git_footers.get_footer_change_id(change_desc.description)[0]
+
+    assert change_desc
+    commits = RunGitSilent(['rev-list', '%s..%s' % (parent,
+                                                    ref_to_push)]).splitlines()
+    if len(commits) > 1:
+      print('WARNING: This will upload %d commits. Run the following command '
+            'to see which commits will be uploaded: ' % len(commits))
+      print('git log %s..%s' % (parent, ref_to_push))
+      print('You can also use `git squash-branch` to squash these into a '
+            'single commit.')
+      ask_for_data('About to upload; enter to confirm.')
+
+    if options.reviewers or options.tbr_owners:
+      change_desc.update_reviewers(options.reviewers, options.tbr_owners,
+                                   change)
+
+    receive_options = []
+    cc = self.GetCCList().split(',')
+    if options.cc:
+      cc.extend(options.cc)
+    cc = filter(None, cc)
+    if cc:
+      receive_options += ['--cc=' + email for email in cc]
+    if change_desc.get_reviewers():
+      receive_options.extend(
+          '--reviewer=' + email for email in change_desc.get_reviewers())
+
+    git_command = ['push']
+    if receive_options:
+      git_command.append('--receive-pack=git receive-pack %s' %
+                         ' '.join(receive_options))
+    git_command += [gerrit_remote, ref_to_push + ':refs/for/' + branch]
+    push_stdout = gclient_utils.CheckCallAndFilter(
+        ['git'] + git_command,
+        print_stdout=True,
+        # Flush after every line: useful for seeing progress when running as
+        # recipe.
+        filter_fn=lambda _: sys.stdout.flush())
+
+    if options.squash:
+      regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*')
+      change_numbers = [m.group(1)
+                        for m in map(regex.match, push_stdout.splitlines())
+                        if m]
+      if len(change_numbers) != 1:
+        DieWithError(
+          ('Created|Updated %d issues on Gerrit, but only 1 expected.\n'
+           'Change-Id: %s') % (len(change_numbers), change_id))
+      self.SetIssue(change_numbers[0])
+      RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(),
+              ref_to_push])
+    return 0
+
+
 
 _CODEREVIEW_IMPLEMENTATIONS = {
   'rietveld': _RietveldChangelistImpl,
@@ -2794,206 +3149,6 @@
   return 'I%s' % change_hash.strip()
 
 
-def GerritUpload(options, args, cl, change):
-  """upload the current branch to gerrit."""
-  # TODO(tandrii): refactor this to be a method of _GerritChangelistImpl,
-  # to avoid private members accessors below.
-
-  # We assume the remote called "origin" is the one we want.
-  # It is probably not worthwhile to support different workflows.
-  gerrit_remote = 'origin'
-
-  remote, remote_branch = cl.GetRemoteBranch()
-  branch = GetTargetRef(remote, remote_branch, options.target_branch,
-                        pending_prefix='')
-
-  if options.title:
-    print "\nPatch titles (-t) are not supported in Gerrit. Aborting..."
-    return 1
-
-  if options.squash:
-    if not cl.GetIssue():
-      # TODO(tandrii): deperecate this after 2016Q2.
-      # Backwards compatibility with shadow branch, which used to contain
-      # change-id for a given branch, using which we can fetch actual issue
-      # number and set it as the property of the branch, which is the new way.
-      message = RunGitSilent(['show', '--format=%B', '-s',
-                              'refs/heads/git_cl_uploads/%s' % cl.GetBranch()])
-      if message:
-        change_ids = git_footers.get_footer_change_id(message.strip())
-        if change_ids and len(change_ids) == 1:
-          details = gerrit_util.GetChangeDetail(
-              cl._codereview_impl._GetGerritHost(), change_ids[0])
-          if details:
-            print('WARNING: found old upload in branch git_cl_uploads/%s '
-                  'corresponding to issue %s' %
-                  (cl.GetBranch(), details['_number']))
-            cl.SetIssue(details['_number'])
-        if not cl.GetIssue():
-          DieWithError(
-              '\n'  # For readability of the blob below.
-              'Found old upload in branch git_cl_uploads/%s, '
-              'but failed to find corresponding Gerrit issue.\n'
-              'If you know the issue number, set it manually first:\n'
-              '    git cl issue 123456\n'
-              'If you intended to upload this CL as new issue, '
-              'just delete or rename the old upload branch:\n'
-              '    git rename-branch git_cl_uploads/%s old_upload-%s\n'
-              'After that, please run git cl upload again.' %
-              tuple([cl.GetBranch()] * 3))
-      # End of backwards compatability.
-
-    if cl.GetIssue():
-      # Try to get the message from a previous upload.
-      message = cl.GetDescription()
-      if not message:
-        DieWithError(
-            'failed to fetch description from current Gerrit issue %d\n'
-            '%s' % (cl.GetIssue(), cl.GetIssueURL()))
-      change_id = cl._codereview_impl._GetChangeDetail([])['change_id']
-      while True:
-        footer_change_ids = git_footers.get_footer_change_id(message)
-        if footer_change_ids == [change_id]:
-          break
-        if not footer_change_ids:
-          message = git_footers.add_footer_change_id(message, change_id)
-          print('WARNING: appended missing Change-Id to issue description')
-          continue
-        # There is already a valid footer but with different or several ids.
-        # Doing this automatically is non-trivial as we don't want to lose
-        # existing other footers, yet we want to append just 1 desired
-        # Change-Id. Thus, just create a new footer, but let user verify the new
-        # description.
-        message = '%s\n\nChange-Id: %s' % (message, change_id)
-        print(
-            'WARNING: issue %s has Change-Id footer(s):\n'
-            '  %s\n'
-            'but issue has Change-Id %s, according to Gerrit.\n'
-            'Please, check the proposed correction to the description, '
-            'and edit it if necessary but keep the "Change-Id: %s" footer\n'
-            % (cl.GetIssue(), '\n  '.join(footer_change_ids), change_id,
-               change_id))
-        ask_for_data('Press enter to edit now, Ctrl+C to abort')
-        if not options.force:
-          change_desc = ChangeDescription(message)
-          change_desc.prompt()
-          message = change_desc.description
-          if not message:
-            DieWithError("Description is empty. Aborting...")
-        # Continue the while loop.
-      # Sanity check of this code - we should end up with proper message footer.
-      assert [change_id] == git_footers.get_footer_change_id(message)
-      change_desc = ChangeDescription(message)
-    else:
-      change_desc = ChangeDescription(
-          options.message or CreateDescriptionFromLog(args))
-      if not options.force:
-        change_desc.prompt()
-      if not change_desc.description:
-        DieWithError("Description is empty. Aborting...")
-      message = change_desc.description
-      change_ids = git_footers.get_footer_change_id(message)
-      if len(change_ids) > 1:
-        DieWithError('too many Change-Id footers, at most 1 allowed.')
-      if not change_ids:
-        # Generate the Change-Id automatically.
-        message = git_footers.add_footer_change_id(
-            message, GenerateGerritChangeId(message))
-        change_desc.set_description(message)
-        change_ids = git_footers.get_footer_change_id(message)
-        assert len(change_ids) == 1
-      change_id = change_ids[0]
-
-    remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch())
-    if remote is '.':
-      # If our upstream branch is local, we base our squashed commit on its
-      # squashed version.
-      upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch)
-      # Check the squashed hash of the parent.
-      parent = RunGit(['config',
-                       'branch.%s.gerritsquashhash' % upstream_branch_name],
-                      error_ok=True).strip()
-      # Verify that the upstream branch has been uploaded too, otherwise
-      # Gerrit will create additional CLs when uploading.
-      if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) !=
-                        RunGitSilent(['rev-parse', parent + ':'])):
-        # TODO(tandrii): remove "old depot_tools" part on April 12, 2016.
-        DieWithError(
-            'Upload upstream branch %s first.\n'
-            'Note: maybe you\'ve uploaded it with --no-squash or with an old\n'
-            '      version of depot_tools. If so, then re-upload it with:\n'
-            '      git cl upload --squash\n' % upstream_branch_name)
-    else:
-      parent = cl.GetCommonAncestorWithUpstream()
-
-    tree = RunGit(['rev-parse', 'HEAD:']).strip()
-    ref_to_push = RunGit(['commit-tree', tree, '-p', parent,
-                          '-m', message]).strip()
-  else:
-    change_desc = ChangeDescription(
-        options.message or CreateDescriptionFromLog(args))
-    if not change_desc.description:
-      DieWithError("Description is empty. Aborting...")
-
-    if not git_footers.get_footer_change_id(change_desc.description):
-      DownloadGerritHook(False)
-      change_desc.set_description(AddChangeIdToCommitMessage(options, args))
-    ref_to_push = 'HEAD'
-    parent = '%s/%s' % (gerrit_remote, branch)
-    change_id = git_footers.get_footer_change_id(change_desc.description)[0]
-
-  assert change_desc
-  commits = RunGitSilent(['rev-list', '%s..%s' % (parent,
-                                                  ref_to_push)]).splitlines()
-  if len(commits) > 1:
-    print('WARNING: This will upload %d commits. Run the following command '
-          'to see which commits will be uploaded: ' % len(commits))
-    print('git log %s..%s' % (parent, ref_to_push))
-    print('You can also use `git squash-branch` to squash these into a single '
-          'commit.')
-    ask_for_data('About to upload; enter to confirm.')
-
-  if options.reviewers or options.tbr_owners:
-    change_desc.update_reviewers(options.reviewers, options.tbr_owners, change)
-
-  receive_options = []
-  cc = cl.GetCCList().split(',')
-  if options.cc:
-    cc.extend(options.cc)
-  cc = filter(None, cc)
-  if cc:
-    receive_options += ['--cc=' + email for email in cc]
-  if change_desc.get_reviewers():
-    receive_options.extend(
-        '--reviewer=' + email for email in change_desc.get_reviewers())
-
-  git_command = ['push']
-  if receive_options:
-    git_command.append('--receive-pack=git receive-pack %s' %
-                       ' '.join(receive_options))
-  git_command += [gerrit_remote, ref_to_push + ':refs/for/' + branch]
-  push_stdout = gclient_utils.CheckCallAndFilter(
-      ['git'] + git_command,
-      print_stdout=True,
-      # Flush after every line: useful for seeing progress when running as
-      # recipe.
-      filter_fn=lambda _: sys.stdout.flush())
-
-  if options.squash:
-    regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*')
-    change_numbers = [m.group(1)
-                      for m in map(regex.match, push_stdout.splitlines())
-                      if m]
-    if len(change_numbers) != 1:
-      DieWithError(
-        ('Created|Updated %d issues on Gerrit, but only 1 expected.\n'
-         'Change-Id: %s') % (len(change_numbers), change_id))
-    cl.SetIssue(change_numbers[0])
-    RunGit(['config', 'branch.%s.gerritsquashhash' % cl.GetBranch(),
-            ref_to_push])
-  return 0
-
-
 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix):
   """Computes the remote branch ref to use for the CL.
 
@@ -3049,156 +3204,6 @@
   return remote_branch
 
 
-def RietveldUpload(options, args, cl, change):
-  """upload the patch to rietveld."""
-  upload_args = ['--assume_yes']  # Don't ask about untracked files.
-  upload_args.extend(['--server', cl.GetCodereviewServer()])
-  # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl.
-  upload_args.extend(auth.auth_config_to_command_options(
-      cl._codereview_impl.GetAuthConfig()))
-  if options.emulate_svn_auto_props:
-    upload_args.append('--emulate_svn_auto_props')
-
-  change_desc = None
-
-  if options.email is not None:
-    upload_args.extend(['--email', options.email])
-
-  if cl.GetIssue():
-    if options.title:
-      upload_args.extend(['--title', options.title])
-    if options.message:
-      upload_args.extend(['--message', options.message])
-    upload_args.extend(['--issue', str(cl.GetIssue())])
-    print ("This branch is associated with issue %s. "
-           "Adding patch to that issue." % cl.GetIssue())
-  else:
-    if options.title:
-      upload_args.extend(['--title', options.title])
-    message = options.title or options.message or CreateDescriptionFromLog(args)
-    change_desc = ChangeDescription(message)
-    if options.reviewers or options.tbr_owners:
-      change_desc.update_reviewers(options.reviewers,
-                                   options.tbr_owners,
-                                   change)
-    if not options.force:
-      change_desc.prompt()
-
-    if not change_desc.description:
-      print "Description is empty; aborting."
-      return 1
-
-    upload_args.extend(['--message', change_desc.description])
-    if change_desc.get_reviewers():
-      upload_args.append('--reviewers=' + ','.join(change_desc.get_reviewers()))
-    if options.send_mail:
-      if not change_desc.get_reviewers():
-        DieWithError("Must specify reviewers to send email.")
-      upload_args.append('--send_mail')
-
-    # We check this before applying rietveld.private assuming that in
-    # rietveld.cc only addresses which we can send private CLs to are listed
-    # if rietveld.private is set, and so we should ignore rietveld.cc only when
-    # --private is specified explicitly on the command line.
-    if options.private:
-      logging.warn('rietveld.cc is ignored since private flag is specified.  '
-                   'You need to review and add them manually if necessary.')
-      cc = cl.GetCCListWithoutDefault()
-    else:
-      cc = cl.GetCCList()
-    cc = ','.join(filter(None, (cc, ','.join(options.cc))))
-    if cc:
-      upload_args.extend(['--cc', cc])
-
-  if options.private or settings.GetDefaultPrivateFlag() == "True":
-    upload_args.append('--private')
-
-  upload_args.extend(['--git_similarity', str(options.similarity)])
-  if not options.find_copies:
-    upload_args.extend(['--git_no_find_copies'])
-
-  # Include the upstream repo's URL in the change -- this is useful for
-  # projects that have their source spread across multiple repos.
-  remote_url = cl.GetGitBaseUrlFromConfig()
-  if not remote_url:
-    if settings.GetIsGitSvn():
-      remote_url = cl.GetGitSvnRemoteUrl()
-    else:
-      if cl.GetRemoteUrl() and '/' in cl.GetUpstreamBranch():
-        remote_url = (cl.GetRemoteUrl() + '@'
-                      + cl.GetUpstreamBranch().split('/')[-1])
-  if remote_url:
-    upload_args.extend(['--base_url', remote_url])
-    remote, remote_branch = cl.GetRemoteBranch()
-    target_ref = GetTargetRef(remote, remote_branch, options.target_branch,
-                              settings.GetPendingRefPrefix())
-    if target_ref:
-      upload_args.extend(['--target_ref', target_ref])
-
-    # Look for dependent patchsets. See crbug.com/480453 for more details.
-    remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch())
-    upstream_branch = ShortBranchName(upstream_branch)
-    if remote is '.':
-      # A local branch is being tracked.
-      local_branch = ShortBranchName(upstream_branch)
-      if settings.GetIsSkipDependencyUpload(local_branch):
-        print
-        print ('Skipping dependency patchset upload because git config '
-               'branch.%s.skip-deps-uploads is set to True.' % local_branch)
-        print
-      else:
-        auth_config = auth.extract_auth_config_from_options(options)
-        branch_cl = Changelist(branchref=local_branch, auth_config=auth_config)
-        branch_cl_issue_url = branch_cl.GetIssueURL()
-        branch_cl_issue = branch_cl.GetIssue()
-        branch_cl_patchset = branch_cl.GetPatchset()
-        if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset:
-          upload_args.extend(
-              ['--depends_on_patchset', '%s:%s' % (
-                   branch_cl_issue, branch_cl_patchset)])
-          print
-          print ('The current branch (%s) is tracking a local branch (%s) with '
-                 'an associated CL.') % (cl.GetBranch(), local_branch)
-          print 'Adding %s/#ps%s as a dependency patchset.' % (
-              branch_cl_issue_url, branch_cl_patchset)
-          print
-
-  project = settings.GetProject()
-  if project:
-    upload_args.extend(['--project', project])
-
-  if options.cq_dry_run:
-    upload_args.extend(['--cq_dry_run'])
-
-  try:
-    upload_args = ['upload'] + upload_args + args
-    logging.info('upload.RealMain(%s)', upload_args)
-    issue, patchset = upload.RealMain(upload_args)
-    issue = int(issue)
-    patchset = int(patchset)
-  except KeyboardInterrupt:
-    sys.exit(1)
-  except:
-    # If we got an exception after the user typed a description for their
-    # change, back up the description before re-raising.
-    if change_desc:
-      backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE)
-      print '\nGot exception while uploading -- saving description to %s\n' \
-          % backup_path
-      backup_file = open(backup_path, 'w')
-      backup_file.write(change_desc.description)
-      backup_file.close()
-    raise
-
-  if not cl.GetIssue():
-    cl.SetIssue(issue)
-  cl.SetPatchset(patchset)
-
-  if options.use_commit_queue:
-    cl.SetFlag('commit', '1')
-  return 0
-
-
 def cleanup_list(l):
   """Fixes a list so that comma separated items are put as individual items.
 
@@ -3349,9 +3354,7 @@
     options.squash = ((settings.GetSquashGerritUploads() or options.squash) and
                       not options.no_squash)
 
-    ret = GerritUpload(options, args, cl, change)
-  else:
-    ret = RietveldUpload(options, args, cl, change)
+  ret = cl.CMDUploadChange(options, args, change)
   if not ret:
     git_set_branch_value('last-upload-hash',
                          RunGit(['rev-parse', 'HEAD']).strip())