git_cherry_pick_upload.py 4.35 KB
Newer Older
1 2 3 4 5 6 7 8
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Upload a cherry pick CL to rietveld."""

import md5
9
import optparse
10 11 12
import subprocess2
import sys

13 14
import auth

15 16 17 18 19 20
from git_cl import Changelist
from git_common import config, run
from third_party.upload import EncodeMultipartFormData, GitVCS
from rietveld import Rietveld


21
def cherry_pick(target_branch, commit, auth_config):
22 23 24 25 26
  """Attempt to upload a cherry pick CL to rietveld.

  Args:
    target_branch: The branch to cherry pick onto.
    commit: The git hash of the commit to cherry pick.
27
    auth_config: auth.AuthConfig object with authentication configuration.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
  """
  author = config('user.email')

  description = '%s\n\n(cherry picked from commit %s)\n' % (
      run('show', '--pretty=%B', '--quiet', commit), commit)

  parent = run('show', '--pretty=%P', '--quiet', commit)
  print 'Found parent revision:', parent

  class Options(object):
    def __init__(self):
      self.emulate_svn_auto_props = False

  content_type, payload = EncodeMultipartFormData([
      ('base', '%s@%s' % (Changelist().GetRemoteUrl(), target_branch)),
      ('cc', config('rietveld.cc')),
      ('content_upload', '1'),
      ('description', description),
      ('project', '%s@%s' % (config('rietveld.project'), target_branch)),
      ('subject', description.splitlines()[0]),
      ('user', author),
  ], [
      ('data', 'data.diff', GitVCS(Options()).PostProcessDiff(
          run('diff', parent, commit))),
  ])

54
  rietveld = Rietveld(config('rietveld.server'), auth_config, author)
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
  # pylint: disable=W0212
  output = rietveld._send(
    '/upload',
    payload=payload,
    content_type=content_type,
  ).splitlines()

  # If successful, output will look like:
  # Issue created. URL: https://codereview.chromium.org/1234567890
  # 1
  # 10001 some/path/first.file
  # 10002 some/path/second.file
  # 10003 some/path/third.file

  if output[0].startswith('Issue created. URL: '):
    print output[0]
    issue = output[0].rsplit('/', 1)[-1]
    patchset = output[1]
    files = output[2:]

    for f in files:
      file_id, filename = f.split()
      mode = 'M'

      try:
        content = run('show', '%s:%s' % (parent, filename))
      except subprocess2.CalledProcessError:
        # File didn't exist in the parent revision.
        content = ''
        mode = 'A'

      content_type, payload = EncodeMultipartFormData([
        ('checksum', md5.md5(content).hexdigest()),
        ('filename', filename),
        ('is_current', 'False'),
        ('status', mode),
      ], [
        ('data', filename, content),
      ])

      # pylint: disable=W0212
      print '  Uploading base file for %s:' % filename, rietveld._send(
        '/%s/upload_content/%s/%s' % (issue, patchset, file_id),
        payload=payload,
        content_type=content_type,
      )

      try:
        content = run('show', '%s:%s' % (commit, filename))
      except subprocess2.CalledProcessError:
        # File no longer exists in the new commit.
        content = ''
        mode = 'D'

      content_type, payload = EncodeMultipartFormData([
        ('checksum', md5.md5(content).hexdigest()),
        ('filename', filename),
        ('is_current', 'True'),
        ('status', mode),
      ], [
        ('data', filename, content),
      ])

      # pylint: disable=W0212
      print '  Uploading %s:' % filename, rietveld._send(
        '/%s/upload_content/%s/%s' % (issue, patchset, file_id),
        payload=payload,
        content_type=content_type,
      )

    # pylint: disable=W0212
    print 'Finalizing upload:', rietveld._send('/%s/upload_complete/1' % issue)


def main():
130 131 132
  parser = optparse.OptionParser(
      usage='usage: %prog --branch <branch> <commit>')
  parser.add_option(
133 134 135
      '--branch',
      '-b',
      help='The upstream branch to cherry pick to.',
136 137 138 139 140 141 142 143 144 145 146
      metavar='<branch>')
  auth.add_auth_options(parser)
  options, args = parser.parse_args()
  auth_config = auth.extract_auth_config_from_options

  if not options.branch:
    parser.error('--branch is required')
  if len(args) != 1:
    parser.error('Expecting single argument <commit>')

  cherry_pick(options.branch, args[0], auth_config)
147 148
  return 0

149 150

if __name__ == '__main__':
151 152 153 154 155
  try:
    sys.exit(main())
  except KeyboardInterrupt:
    sys.stderr.write('interrupted\n')
    sys.exit(1)