git_cherry_pick_upload.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env python
  2. # Copyright 2014 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Upload a cherry pick CL to rietveld."""
  6. import md5
  7. import optparse
  8. import subprocess2
  9. import sys
  10. import auth
  11. from git_cl import Changelist
  12. from git_common import config, run
  13. from third_party.upload import EncodeMultipartFormData, GitVCS
  14. from rietveld import Rietveld
  15. def cherry_pick(target_branch, commit, auth_config):
  16. """Attempt to upload a cherry pick CL to rietveld.
  17. Args:
  18. target_branch: The branch to cherry pick onto.
  19. commit: The git hash of the commit to cherry pick.
  20. auth_config: auth.AuthConfig object with authentication configuration.
  21. """
  22. author = config('user.email')
  23. description = '%s\n\n(cherry picked from commit %s)\n' % (
  24. run('show', '--pretty=%B', '--quiet', commit), commit)
  25. parent = run('show', '--pretty=%P', '--quiet', commit)
  26. print 'Found parent revision:', parent
  27. class Options(object):
  28. def __init__(self):
  29. self.emulate_svn_auto_props = False
  30. content_type, payload = EncodeMultipartFormData([
  31. ('base', '%s@%s' % (Changelist().GetRemoteUrl(), target_branch)),
  32. ('cc', config('rietveld.cc')),
  33. ('content_upload', '1'),
  34. ('description', description),
  35. ('project', '%s@%s' % (config('rietveld.project'), target_branch)),
  36. ('subject', description.splitlines()[0]),
  37. ('user', author),
  38. ], [
  39. ('data', 'data.diff', GitVCS(Options()).PostProcessDiff(
  40. run('diff', parent, commit))),
  41. ])
  42. rietveld = Rietveld(config('rietveld.server'), auth_config, author)
  43. # pylint: disable=protected-access
  44. output = rietveld._send(
  45. '/upload',
  46. payload=payload,
  47. content_type=content_type,
  48. ).splitlines()
  49. # If successful, output will look like:
  50. # Issue created. URL: https://codereview.chromium.org/1234567890
  51. # 1
  52. # 10001 some/path/first.file
  53. # 10002 some/path/second.file
  54. # 10003 some/path/third.file
  55. if output[0].startswith('Issue created. URL: '):
  56. print output[0]
  57. issue = output[0].rsplit('/', 1)[-1]
  58. patchset = output[1]
  59. files = output[2:]
  60. for f in files:
  61. file_id, filename = f.split()
  62. mode = 'M'
  63. try:
  64. content = run('show', '%s:%s' % (parent, filename))
  65. except subprocess2.CalledProcessError:
  66. # File didn't exist in the parent revision.
  67. content = ''
  68. mode = 'A'
  69. content_type, payload = EncodeMultipartFormData([
  70. ('checksum', md5.md5(content).hexdigest()),
  71. ('filename', filename),
  72. ('is_current', 'False'),
  73. ('status', mode),
  74. ], [
  75. ('data', filename, content),
  76. ])
  77. # pylint: disable=protected-access
  78. print ' Uploading base file for %s:' % filename, rietveld._send(
  79. '/%s/upload_content/%s/%s' % (issue, patchset, file_id),
  80. payload=payload,
  81. content_type=content_type,
  82. )
  83. try:
  84. content = run('show', '%s:%s' % (commit, filename))
  85. except subprocess2.CalledProcessError:
  86. # File no longer exists in the new commit.
  87. content = ''
  88. mode = 'D'
  89. content_type, payload = EncodeMultipartFormData([
  90. ('checksum', md5.md5(content).hexdigest()),
  91. ('filename', filename),
  92. ('is_current', 'True'),
  93. ('status', mode),
  94. ], [
  95. ('data', filename, content),
  96. ])
  97. # pylint: disable=protected-access
  98. print ' Uploading %s:' % filename, rietveld._send(
  99. '/%s/upload_content/%s/%s' % (issue, patchset, file_id),
  100. payload=payload,
  101. content_type=content_type,
  102. )
  103. # pylint: disable=protected-access
  104. print 'Finalizing upload:', rietveld._send('/%s/upload_complete/1' % issue)
  105. def main():
  106. parser = optparse.OptionParser(
  107. usage='usage: %prog --branch <branch> <commit>')
  108. parser.add_option(
  109. '--branch',
  110. '-b',
  111. help='The upstream branch to cherry pick to.',
  112. metavar='<branch>')
  113. auth.add_auth_options(parser)
  114. options, args = parser.parse_args()
  115. auth_config = auth.extract_auth_config_from_options
  116. if not options.branch:
  117. parser.error('--branch is required')
  118. if len(args) != 1:
  119. parser.error('Expecting single argument <commit>')
  120. cherry_pick(options.branch, args[0], auth_config)
  121. return 0
  122. if __name__ == '__main__':
  123. try:
  124. sys.exit(main())
  125. except KeyboardInterrupt:
  126. sys.stderr.write('interrupted\n')
  127. sys.exit(1)