Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(435)

Side by Side Diff: trychange_git.py

Issue 1003733006: Splitting trychange for git_try and removing no longer needed support for: (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tests/trychange_git_unittest.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Client-side script to send a try job to the try server. It communicates to 6 """Client-side script to send a try job to the try server. It communicates to
7 the try server by either writting to a svn/git repository or by directly 7 the try server by either writting to a svn/git repository or by directly
8 connecting to the server by HTTP. 8 connecting to the server by HTTP.
9 """ 9 """
10 10
11 import contextlib 11 import contextlib
12 import datetime 12 import datetime
13 import errno 13 import errno
14 import getpass 14 import getpass
15 import itertools 15 import itertools
16 import json 16 import json
17 import logging 17 import logging
18 import optparse 18 import optparse
19 import os 19 import os
20 import posixpath 20 import posixpath
21 import re 21 import re
22 import shutil 22 import shutil
23 import sys 23 import sys
24 import tempfile 24 import tempfile
25 import urllib 25 import urllib
26 import urllib2 26 import urllib2
27 import urlparse 27 import urlparse
28 28
29 import breakpad # pylint: disable=W0611
30
31 import fix_encoding
32 import gcl
33 import gclient_utils 29 import gclient_utils
34 import gerrit_util 30 import git_cl
35 import scm 31 import scm
36 import subprocess2 32 import subprocess2
37 33
38 34
39 __version__ = '1.2' 35 __version__ = '1.2'
40 36
41 37
42 # Constants 38 # Constants
43 HELP_STRING = "Sorry, Tryserver is not available." 39 HELP_STRING = "Sorry, Tryserver is not available."
44 USAGE = r"""%prog [options] 40 USAGE = r"""%prog [options]
45 41
46 Client-side script to send a try job to the try server. It communicates to 42 Client-side script to send a try job to the try server. It communicates to
47 the try server by either writting to a svn repository or by directly connecting 43 the try server by writting to a svn repository."""
48 to the server by HTTP."""
49 44
50 EPILOG = """ 45 EPILOG = """
51 Examples: 46 Examples:
52 Send a patch directly from rietveld: 47 Send a patch directly from rietveld:
53 %(prog)s -R codereview.chromium.org/1337 48 %(prog)s -R codereview.chromium.org/1337
54 --email recipient@example.com --root src 49 --email recipient@example.com --root src
55 50
56 Try a change against a particular revision: 51 Try a change against a particular revision:
57 %(prog)s -r 123 52 %(prog)s -r 123
58 53
59 Try a change including changes to a sub repository: 54 Try a change including changes to a sub repository:
60 %(prog)s -s third_party/WebKit 55 %(prog)s -s third_party/WebKit
61 56
62 A git patch off a web site (git inserts a/ and b/) and fix the base dir: 57 A git patch off a web site (git inserts a/ and b/) and fix the base dir:
63 %(prog)s --url http://url/to/patch.diff --patchlevel 1 --root src 58 %(prog)s --url http://url/to/patch.diff --patchlevel 1 --root src
64 59
65 Use svn to store the try job, specify an alternate email address and use a 60 Use svn to store the try job, specify an alternate email address and use a
66 premade diff file on the local drive: 61 premade diff file on the local drive:
67 %(prog)s --email user@example.com 62 %(prog)s --email user@example.com
68 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff 63 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff
69 64
70 Running only on a 'mac' slave with revision 123 and clobber first; specify 65 Running only on a 'mac' slave with revision 123 and clobber first; specify
71 manually the 3 source files to use for the try job: 66 manually the 3 source files to use for the try job:
72 %(prog)s --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h 67 %(prog)s --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h
73 -f include/b.h 68 -f include/b.h
74 """ 69 """
75 70
76 GIT_PATCH_DIR_BASENAME = os.path.join('git-try', 'patches-git') 71 GIT_PATCH_DIR_BASENAME = os.path.join('git-try', 'patches-git')
77 GIT_BRANCH_FILE = 'ref' 72 GIT_BRANCH_FILE = 'ref'
78 _GIT_PUSH_ATTEMPTS = 3 73
79 74
80 def DieWithError(message): 75 def DieWithError(message):
81 print >> sys.stderr, message 76 print >> sys.stderr, message
82 sys.exit(1) 77 sys.exit(1)
83 78
84 79
85 def RunCommand(args, error_ok=False, error_message=None, **kwargs): 80 def RunCommand(args, error_ok=False, error_message=None, **kwargs):
86 try: 81 try:
87 return subprocess2.check_output(args, shell=False, **kwargs) 82 return subprocess2.check_output(args, shell=False, **kwargs)
88 except subprocess2.CalledProcessError, e: 83 except subprocess2.CalledProcessError, e:
89 if not error_ok: 84 if not error_ok:
90 DieWithError( 85 DieWithError(
91 'Command "%s" failed.\n%s' % ( 86 'Command "%s" failed.\n%s' % (
92 ' '.join(args), error_message or e.stdout or '')) 87 ' '.join(args), error_message or e.stdout or ''))
93 return e.stdout 88 return e.stdout
94 89
95 90
96 def RunGit(args, **kwargs): 91 def RunGit(args, **kwargs):
97 """Returns stdout.""" 92 """Returns stdout."""
98 return RunCommand(['git'] + args, **kwargs) 93 return RunCommand(['git'] + args, **kwargs)
99 94
95
100 class Error(Exception): 96 class Error(Exception):
101 """An error during a try job submission. 97 """An error during a try job submission.
102 98
103 For this error, trychange.py does not display stack trace, only message 99 For this error, trychange.py does not display stack trace, only message
104 """ 100 """
105 101
106 class InvalidScript(Error):
107 def __str__(self):
108 return self.args[0] + '\n' + HELP_STRING
109
110 102
111 class NoTryServerAccess(Error): 103 class NoTryServerAccess(Error):
112 def __str__(self): 104 def __str__(self):
113 return self.args[0] + '\n' + HELP_STRING 105 return self.args[0] + '\n' + HELP_STRING
114 106
107
115 def Escape(name): 108 def Escape(name):
116 """Escapes characters that could interfere with the file system or try job 109 """Escapes characters that could interfere with the file system or try job
117 parsing. 110 parsing.
118 """ 111 """
119 return re.sub(r'[^\w#-]', '_', name) 112 return re.sub(r'[^\w#-]', '_', name)
120 113
121 114
122 class SCM(object): 115 class GIT(object):
123 """Simplistic base class to implement one function: ProcessOptions.""" 116
117 """Gathers the options and diff for a git checkout."""
124 def __init__(self, options, path, file_list): 118 def __init__(self, options, path, file_list):
125 items = path.split('@') 119 items = path.split('@')
126 assert len(items) <= 2 120 assert len(items) <= 2
127 self.checkout_root = os.path.abspath(items[0]) 121 self.checkout_root = os.path.abspath(items[0])
128 items.append(None) 122 items.append(None)
129 self.diff_against = items[1] 123 self.diff_against = items[1]
130 self.options = options 124 self.options = options
131 # Lazy-load file list from the SCM unless files were specified in options. 125 # Lazy-load file list from the SCM unless files were specified in options.
132 self._files = None 126 self._files = None
133 self._file_tuples = None 127 self._file_tuples = None
134 if file_list: 128 if file_list:
135 self._files = file_list 129 self._files = file_list
136 self._file_tuples = [('M', f) for f in self.files] 130 self._file_tuples = [('M', f) for f in self.files]
137 self.options.files = None 131 self.options.files = None
138 self.codereview_settings = None 132 self.codereview_settings = None
139 self.codereview_settings_file = 'codereview.settings' 133 self.codereview_settings_file = 'codereview.settings'
140 self.toplevel_root = None 134 self.toplevel_root = None
141 135 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
142 def GetFileNames(self): 136 if not self.options.name:
143 """Return the list of files in the diff.""" 137 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
144 return self.files 138 if not self.options.email:
139 self.options.email = scm.GIT.GetEmail(self.checkout_root)
140 if not self.diff_against:
141 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root)
142 if not self.diff_against:
143 raise NoTryServerAccess(
144 "Unable to determine default branch to diff against. "
145 "Verify this branch is set up to track another"
146 "(via the --track argument to \"git checkout -b ...\"")
147 logging.info("GIT(%s)" % self.checkout_root)
145 148
146 def GetCodeReviewSetting(self, key): 149 def GetCodeReviewSetting(self, key):
147 """Returns a value for the given key for this repository.
148
149 Uses gcl-style settings from the repository.
150 """
151 if gcl:
152 gcl_setting = gcl.GetCodeReviewSetting(key)
153 if gcl_setting != '':
154 return gcl_setting
155 if self.codereview_settings is None: 150 if self.codereview_settings is None:
156 self.codereview_settings = {} 151 self.codereview_settings = {}
157 settings_file = self.ReadRootFile(self.codereview_settings_file) 152 settings_file = self.ReadRootFile(self.codereview_settings_file)
158 if settings_file: 153 if settings_file:
159 for line in settings_file.splitlines(): 154 for line in settings_file.splitlines():
160 if not line or line.lstrip().startswith('#'): 155 if not line or line.lstrip().startswith('#'):
161 continue 156 continue
162 k, v = line.split(":", 1) 157 k, v = line.split(":", 1)
163 self.codereview_settings[k.strip()] = v.strip() 158 self.codereview_settings[k.strip()] = v.strip()
164 return self.codereview_settings.get(key, '') 159 return self.codereview_settings.get(key, '')
165 160
166 def _GclStyleSettings(self): 161 def _GclStyleSettings(self):
167 """Set default settings based on the gcl-style settings from the repository. 162 """Set default settings based on the gcl-style settings from the repository.
168 163
169 The settings in the self.options object will only be set if no previous 164 The settings in the self.options object will only be set if no previous
170 value exists (i.e. command line flags to the try command will override the 165 value exists (i.e. command line flags to the try command will override the
171 settings in codereview.settings). 166 settings in codereview.settings).
172 """ 167 """
173 settings = { 168 settings = {
174 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'),
175 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'),
176 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'), 169 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'),
177 'gerrit_url': self.GetCodeReviewSetting('TRYSERVER_GERRIT_URL'),
178 'git_repo': self.GetCodeReviewSetting('TRYSERVER_GIT_URL'),
179 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'), 170 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'),
180 # Primarily for revision=auto 171 # Primarily for revision=auto
181 'revision': self.GetCodeReviewSetting('TRYSERVER_REVISION'), 172 'revision': self.GetCodeReviewSetting('TRYSERVER_REVISION'),
182 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'), 173 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'),
183 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'), 174 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'),
184 } 175 }
185 logging.info('\n'.join(['%s: %s' % (k, v) 176 logging.info('\n'.join(['%s: %s' % (k, v)
186 for (k, v) in settings.iteritems() if v])) 177 for (k, v) in settings.iteritems() if v]))
187 for (k, v) in settings.iteritems(): 178 for (k, v) in settings.iteritems():
188 # Avoid overwriting options already set using command line flags. 179 # Avoid overwriting options already set using command line flags.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 return True 228 return True
238 for r in self.options.exclude: 229 for r in self.options.exclude:
239 if re.search(r, f[1]): 230 if re.search(r, f[1]):
240 logging.info('Ignoring "%s"' % f[1]) 231 logging.info('Ignoring "%s"' % f[1])
241 return True 232 return True
242 return False 233 return False
243 234
244 self._file_tuples = [f for f in file_tuples if not Excluded(f)] 235 self._file_tuples = [f for f in file_tuples if not Excluded(f)]
245 self._files = [f[1] for f in self._file_tuples] 236 self._files = [f[1] for f in self._file_tuples]
246 237
247 def CaptureStatus(self):
248 """Returns the 'svn status' emulated output as an array of (status, file)
249 tuples."""
250 raise NotImplementedError(
251 "abstract method -- subclass %s must override" % self.__class__)
252
253 @property 238 @property
254 def files(self): 239 def files(self):
255 if self._files is None: 240 if self._files is None:
256 self._SetFileTuples(self.CaptureStatus()) 241 self._SetFileTuples(self.CaptureStatus())
257 return self._files 242 return self._files
258 243
259 @property 244 @property
260 def file_tuples(self): 245 def file_tuples(self):
261 if self._file_tuples is None: 246 if self._file_tuples is None:
262 self._SetFileTuples(self.CaptureStatus()) 247 self._SetFileTuples(self.CaptureStatus())
263 return self._file_tuples 248 return self._file_tuples
264 249
265
266 class SVN(SCM):
267 """Gathers the options and diff for a subversion checkout."""
268 def __init__(self, *args, **kwargs):
269 SCM.__init__(self, *args, **kwargs)
270 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root)
271 if not self.options.email:
272 # Assumes the svn credential is an email address.
273 self.options.email = scm.SVN.GetEmail(self.checkout_root)
274 logging.info("SVN(%s)" % self.checkout_root)
275
276 def ReadRootFile(self, filename):
277 data = SCM.ReadRootFile(self, filename)
278 if data:
279 return data
280
281 # Try to search on the subversion repository for the file.
282 if not gcl:
283 return None
284 data = gcl.GetCachedFile(filename)
285 logging.debug('%s:\n%s' % (filename, data))
286 return data
287
288 def CaptureStatus(self):
289 return scm.SVN.CaptureStatus(None, self.checkout_root)
290
291 def GenerateDiff(self):
292 """Returns a string containing the diff for the given file list.
293
294 The files in the list should either be absolute paths or relative to the
295 given root.
296 """
297 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True,
298 revision=self.diff_against)
299
300
301 class GIT(SCM):
302 """Gathers the options and diff for a git checkout."""
303 def __init__(self, *args, **kwargs):
304 SCM.__init__(self, *args, **kwargs)
305 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
306 if not self.options.name:
307 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
308 if not self.options.email:
309 self.options.email = scm.GIT.GetEmail(self.checkout_root)
310 if not self.diff_against:
311 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root)
312 if not self.diff_against:
313 raise NoTryServerAccess(
314 "Unable to determine default branch to diff against. "
315 "Verify this branch is set up to track another"
316 "(via the --track argument to \"git checkout -b ...\"")
317 logging.info("GIT(%s)" % self.checkout_root)
318
319 def CaptureStatus(self): 250 def CaptureStatus(self):
320 return scm.GIT.CaptureStatus( 251 return scm.GIT.CaptureStatus(
321 [], 252 [],
322 self.checkout_root.replace(os.sep, '/'), 253 self.checkout_root.replace(os.sep, '/'),
323 self.diff_against) 254 self.diff_against)
324 255
325 def GenerateDiff(self): 256 def GenerateDiff(self):
326 if RunGit(['diff-index', 'HEAD']): 257 if RunGit(['diff-index', 'HEAD']):
327 print 'Cannot try with a dirty tree. You must commit locally first.' 258 print 'Cannot try with a dirty tree. You must commit locally first.'
328 return None 259 return None
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 changed_files = checkouts[0].file_tuples 316 changed_files = checkouts[0].file_tuples
386 change = presubmit_support.Change(options.name, 317 change = presubmit_support.Change(options.name,
387 '', 318 '',
388 checkouts[0].checkout_root, 319 checkouts[0].checkout_root,
389 changed_files, 320 changed_files,
390 options.issue, 321 options.issue,
391 options.patchset, 322 options.patchset,
392 options.email) 323 options.email)
393 masters = presubmit_support.DoGetTryMasters( 324 masters = presubmit_support.DoGetTryMasters(
394 change, 325 change,
395 checkouts[0].GetFileNames(), 326 checkouts[0].files,
396 checkouts[0].checkout_root, 327 checkouts[0].checkout_root,
397 root_presubmit, 328 root_presubmit,
398 options.project, 329 options.project,
399 options.verbose, 330 options.verbose,
400 sys.stdout) 331 sys.stdout)
401 332
402 # Compatibility for old checkouts and bots that were on tryserver.chromium. 333 # Compatibility for old checkouts and bots that were on tryserver.chromium.
403 trybots = masters.get('tryserver.chromium', []) 334 trybots = masters.get('tryserver.chromium', [])
404 335
405 # Compatibility for checkouts that are not using tryserver.chromium 336 # Compatibility for checkouts that are not using tryserver.chromium
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 # because it used to have lower-case 'true'. 382 # because it used to have lower-case 'true'.
452 if options.clobber: 383 if options.clobber:
453 values.append(('clobber', 'true')) 384 values.append(('clobber', 'true'))
454 385
455 for bot, tests in bot_spec: 386 for bot, tests in bot_spec:
456 values.append(('bot', ('%s:%s' % (bot, ','.join(tests))))) 387 values.append(('bot', ('%s:%s' % (bot, ','.join(tests)))))
457 388
458 return values 389 return values
459 390
460 391
461 def _SendChangeHTTP(bot_spec, options):
462 """Send a change to the try server using the HTTP protocol."""
463 if not options.host:
464 raise NoTryServerAccess('Please use the --host option to specify the try '
465 'server host to connect to.')
466 if not options.port:
467 raise NoTryServerAccess('Please use the --port option to specify the try '
468 'server port to connect to.')
469
470 values = _ParseSendChangeOptions(bot_spec, options)
471 values.append(('patch', options.diff))
472
473 url = 'http://%s:%s/send_try_patch' % (options.host, options.port)
474
475 logging.info('Sending by HTTP')
476 logging.info(''.join("%s=%s\n" % (k, v) for k, v in values))
477 logging.info(url)
478 logging.info(options.diff)
479 if options.dry_run:
480 return
481
482 try:
483 logging.info('Opening connection...')
484 connection = urllib2.urlopen(url, urllib.urlencode(values))
485 logging.info('Done')
486 except IOError, e:
487 logging.info(str(e))
488 if bot_spec and len(e.args) > 2 and e.args[2] == 'got a bad status line':
489 raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url)
490 else:
491 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url,
492 str(e.args)))
493 if not connection:
494 raise NoTryServerAccess('%s is unaccessible.' % url)
495 logging.info('Reading response...')
496 response = connection.read()
497 logging.info('Done')
498 if response != 'OK':
499 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response))
500
501 PrintSuccess(bot_spec, options)
502
503 @contextlib.contextmanager 392 @contextlib.contextmanager
504 def _TempFilename(name, contents=None): 393 def _TempFilename(name, contents=None):
505 """Create a temporary directory, append the specified name and yield. 394 """Create a temporary directory, append the specified name and yield.
506 395
507 In contrast to NamedTemporaryFile, does not keep the file open. 396 In contrast to NamedTemporaryFile, does not keep the file open.
508 Deletes the file on __exit__. 397 Deletes the file on __exit__.
509 """ 398 """
510 temp_dir = tempfile.mkdtemp(prefix=name) 399 temp_dir = tempfile.mkdtemp(prefix=name)
511 try: 400 try:
512 path = os.path.join(temp_dir, name) 401 path = os.path.join(temp_dir, name)
(...skipping 18 matching lines...) Expand all
531 name (of patch) and diff (contents of patch). 420 name (of patch) and diff (contents of patch).
532 """ 421 """
533 current_time = str(datetime.datetime.now()).replace(':', '.') 422 current_time = str(datetime.datetime.now()).replace(':', '.')
534 patch_basename = '%s.%s.%s.diff' % (Escape(options.user), 423 patch_basename = '%s.%s.%s.diff' % (Escape(options.user),
535 Escape(options.name), current_time) 424 Escape(options.name), current_time)
536 with _TempFilename('description', description) as description_filename: 425 with _TempFilename('description', description) as description_filename:
537 with _TempFilename(patch_basename, options.diff) as patch_filename: 426 with _TempFilename(patch_basename, options.diff) as patch_filename:
538 yield patch_filename, description_filename 427 yield patch_filename, description_filename
539 428
540 429
541 def _SendChangeSVN(bot_spec, options): 430 def _SendChangeSVN(bot_spec, options):
iannucci 2015/03/16 23:33:49 I would take the opportunity to break this out as
542 """Send a change to the try server by committing a diff file on a subversion 431 """Send a change to the try server by committing a diff file on a subversion
543 server.""" 432 server."""
544 if not options.svn_repo: 433 if not options.svn_repo:
545 raise NoTryServerAccess('Please use the --svn_repo option to specify the' 434 raise NoTryServerAccess('Please use the --svn_repo option to specify the'
546 ' try server svn repository to connect to.') 435 ' try server svn repository to connect to.')
547 436
548 values = _ParseSendChangeOptions(bot_spec, options) 437 values = _ParseSendChangeOptions(bot_spec, options)
549 description = ''.join("%s=%s\n" % (k, v) for k, v in values) 438 description = ''.join("%s=%s\n" % (k, v) for k, v in values)
550 logging.info('Sending by SVN') 439 logging.info('Sending by SVN')
551 logging.info(description) 440 logging.info(description)
(...skipping 19 matching lines...) Expand all
571 if scm.SVN.AssertVersion("1.5")[0]: 460 if scm.SVN.AssertVersion("1.5")[0]:
572 command.append('--no-ignore') 461 command.append('--no-ignore')
573 462
574 try: 463 try:
575 subprocess2.check_call(command) 464 subprocess2.check_call(command)
576 except subprocess2.CalledProcessError, e: 465 except subprocess2.CalledProcessError, e:
577 raise NoTryServerAccess(str(e)) 466 raise NoTryServerAccess(str(e))
578 467
579 PrintSuccess(bot_spec, options) 468 PrintSuccess(bot_spec, options)
580 469
470
581 def _GetPatchGitRepo(git_url): 471 def _GetPatchGitRepo(git_url):
582 """Gets a path to a Git repo with patches. 472 """Gets a path to a Git repo with patches.
583 473
584 Stores patches in .git/git-try/patches-git directory, a git repo. If it 474 Stores patches in .git/git-try/patches-git directory, a git repo. If it
585 doesn't exist yet or its origin URL is different, cleans up and clones it. 475 doesn't exist yet or its origin URL is different, cleans up and clones it.
586 If it existed before, then pulls changes. 476 If it existed before, then pulls changes.
587 477
588 Does not support SVN repo. 478 Does not support SVN repo.
589 479
590 Returns a path to the directory with patches. 480 Returns a path to the directory with patches.
(...skipping 21 matching lines...) Expand all
612 else: 502 else:
613 if scm.GIT.IsWorkTreeDirty(patch_dir): 503 if scm.GIT.IsWorkTreeDirty(patch_dir):
614 logging.info('Work dir is dirty: hard reset!') 504 logging.info('Work dir is dirty: hard reset!')
615 scm.GIT.Capture(['reset', '--hard'], cwd=patch_dir) 505 scm.GIT.Capture(['reset', '--hard'], cwd=patch_dir)
616 logging.info('Updating patch repo') 506 logging.info('Updating patch repo')
617 scm.GIT.Capture(['pull', 'origin', 'master'], cwd=patch_dir) 507 scm.GIT.Capture(['pull', 'origin', 'master'], cwd=patch_dir)
618 508
619 return os.path.abspath(patch_dir) 509 return os.path.abspath(patch_dir)
620 510
621 511
622 def _SendChangeGit(bot_spec, options):
623 """Sends a change to the try server by committing a diff file to a GIT repo.
624
625 Creates a temp orphan branch, commits patch.diff, creates a ref pointing to
626 that commit, deletes the temp branch, checks master out, adds 'ref' file
627 containing the name of the new ref, pushes master and the ref to the origin.
628
629 TODO: instead of creating a temp branch, use git-commit-tree.
630 """
631
632 if not options.git_repo:
633 raise NoTryServerAccess('Please use the --git_repo option to specify the '
634 'try server git repository to connect to.')
635
636 values = _ParseSendChangeOptions(bot_spec, options)
637 comment_subject = '%s.%s' % (options.user, options.name)
638 comment_body = ''.join("%s=%s\n" % (k, v) for k, v in values)
639 description = '%s\n\n%s' % (comment_subject, comment_body)
640 logging.info('Sending by GIT')
641 logging.info(description)
642 logging.info(options.git_repo)
643 logging.info(options.diff)
644 if options.dry_run:
645 return
646
647 patch_dir = _GetPatchGitRepo(options.git_repo)
648 def patch_git(*args):
649 return scm.GIT.Capture(list(args), cwd=patch_dir)
650 def add_and_commit(filename, comment_filename):
651 patch_git('add', filename)
652 patch_git('commit', '-F', comment_filename)
653
654 assert scm.GIT.IsInsideWorkTree(patch_dir)
655 assert not scm.GIT.IsWorkTreeDirty(patch_dir)
656
657 with _PrepareDescriptionAndPatchFiles(description, options) as (
658 patch_filename, description_filename):
659 logging.info('Committing patch')
660
661 temp_branch = 'tmp_patch'
662 target_ref = 'refs/patches/%s/%s' % (
663 Escape(options.user),
664 os.path.basename(patch_filename).replace(' ','_'))
665 target_filename = os.path.join(patch_dir, 'patch.diff')
666 branch_file = os.path.join(patch_dir, GIT_BRANCH_FILE)
667
668 patch_git('checkout', 'master')
669 try:
670 # Try deleting an existing temp branch, if any.
671 try:
672 patch_git('branch', '-D', temp_branch)
673 logging.debug('Deleted an existing temp branch.')
674 except subprocess2.CalledProcessError:
675 pass
676 # Create a new branch and put the patch there.
677 patch_git('checkout', '--orphan', temp_branch)
678 patch_git('reset')
679 patch_git('clean', '-f')
680 shutil.copyfile(patch_filename, target_filename)
681 add_and_commit(target_filename, description_filename)
682 assert not scm.GIT.IsWorkTreeDirty(patch_dir)
683
684 # Create a ref and point it to the commit referenced by temp_branch.
685 patch_git('update-ref', target_ref, temp_branch)
686
687 # Delete the temp ref.
688 patch_git('checkout', 'master')
689 patch_git('branch', '-D', temp_branch)
690
691 # Update the branch file in the master.
692 def update_branch():
693 with open(branch_file, 'w') as f:
694 f.write(target_ref)
695 add_and_commit(branch_file, description_filename)
696
697 update_branch()
698
699 # Push master and target_ref to origin.
700 logging.info('Pushing patch')
701 for attempt in xrange(_GIT_PUSH_ATTEMPTS):
702 try:
703 patch_git('push', 'origin', 'master', target_ref)
704 except subprocess2.CalledProcessError as e:
705 is_last = attempt == _GIT_PUSH_ATTEMPTS - 1
706 if is_last:
707 raise NoTryServerAccess(str(e))
708 # Fetch, reset, update branch file again.
709 patch_git('fetch', 'origin')
710 patch_git('reset', '--hard', 'origin/master')
711 update_branch()
712 except subprocess2.CalledProcessError, e:
713 # Restore state.
714 patch_git('checkout', 'master')
715 patch_git('reset', '--hard', 'origin/master')
716 raise
717
718 PrintSuccess(bot_spec, options)
719
720 def _SendChangeGerrit(bot_spec, options):
721 """Posts a try job to a Gerrit change.
722
723 Reads Change-Id from the HEAD commit, resolves the current revision, checks
724 that local revision matches the uploaded one, posts a try job in form of a
725 message, sets Tryjob-Request label to 1.
726
727 Gerrit message format: starts with !tryjob, optionally followed by a tryjob
728 definition in JSON format:
729 buildNames: list of strings specifying build names.
730 """
731
732 logging.info('Sending by Gerrit')
733 if not options.gerrit_url:
734 raise NoTryServerAccess('Please use --gerrit_url option to specify the '
735 'Gerrit instance url to connect to')
736 gerrit_host = urlparse.urlparse(options.gerrit_url).hostname
737 logging.debug('Gerrit host: %s' % gerrit_host)
738
739 def GetChangeId(commmitish):
740 """Finds Change-ID of the HEAD commit."""
741 CHANGE_ID_RGX = '^Change-Id: (I[a-f0-9]{10,})'
742 comment = scm.GIT.Capture(['log', '-1', commmitish, '--format=%b'],
743 cwd=os.getcwd())
744 change_id_match = re.search(CHANGE_ID_RGX, comment, re.I | re.M)
745 if not change_id_match:
746 raise Error('Change-Id was not found in the HEAD commit. Make sure you '
747 'have a Git hook installed that generates and inserts a '
748 'Change-Id into a commit message automatically.')
749 change_id = change_id_match.group(1)
750 return change_id
751
752 def FormatMessage():
753 # Build job definition.
754 job_def = {}
755 builderNames = [builder for builder, _ in bot_spec]
756 if builderNames:
757 job_def['builderNames'] = builderNames
758
759 # Format message.
760 msg = '!tryjob'
761 if job_def:
762 msg = '%s %s' % (msg, json.dumps(job_def, sort_keys=True))
763 return msg
764
765 def PostTryjob(message):
766 logging.info('Posting gerrit message: %s' % message)
767 if not options.dry_run:
768 # Post a message and set TryJob=1 label.
769 try:
770 gerrit_util.SetReview(gerrit_host, change_id, msg=message,
771 labels={'Tryjob-Request': 1})
772 except gerrit_util.GerritError, e:
773 if e.http_status == 400:
774 raise Error(e.message)
775 else:
776 raise
777
778 head_sha = scm.GIT.Capture(['log', '-1', '--format=%H'], cwd=os.getcwd())
779
780 change_id = GetChangeId(head_sha)
781
782 try:
783 # Check that the uploaded revision matches the local one.
784 changes = gerrit_util.GetChangeCurrentRevision(gerrit_host, change_id)
785 except gerrit_util.GerritAuthenticationError, e:
786 raise NoTryServerAccess(e.message)
787
788 assert len(changes) <= 1, 'Multiple changes with id %s' % change_id
789 if not changes:
790 raise Error('A change %s was not found on the server. Was it uploaded?' %
791 change_id)
792 logging.debug('Found Gerrit change: %s' % changes[0])
793 if changes[0]['current_revision'] != head_sha:
794 raise Error('Please upload your latest local changes to Gerrit.')
795
796 # Post a try job.
797 message = FormatMessage()
798 PostTryjob(message)
799 change_url = urlparse.urljoin(options.gerrit_url,
800 '/#/c/%s' % changes[0]['_number'])
801 print('A tryjob was posted on change %s' % change_url)
802
803 def PrintSuccess(bot_spec, options): 512 def PrintSuccess(bot_spec, options):
804 if not options.dry_run: 513 if not options.dry_run:
805 text = 'Patch \'%s\' sent to try server' % options.name 514 text = 'Patch \'%s\' sent to try server' % options.name
806 if bot_spec: 515 if bot_spec:
807 text += ': %s' % ', '.join( 516 text += ': %s' % ', '.join(
808 '%s:%s' % (b[0], ','.join(b[1])) for b in bot_spec) 517 '%s:%s' % (b[0], ','.join(b[1])) for b in bot_spec)
809 print(text) 518 print(text)
810 519
811 520
812 def GuessVCS(options, path, file_list): 521 def DetectGit(options, path, file_list):
813 """Helper to guess the version control system. 522 """Instantiates the GIT class if a git repo is detected.
814
815 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't
816 support it yet.
817
818 This examines the path directory, guesses which SCM we're using, and
819 returns an instance of the appropriate class. Exit with an error if we can't
820 figure it out.
821 523
822 Returns: 524 Returns:
823 A SCM instance. Exits if the SCM can't be guessed. 525 A GIT instance. None if repo not found.
824 """ 526 """
825 __pychecker__ = 'no-returnvalues' 527 __pychecker__ = 'no-returnvalues'
826 real_path = path.split('@')[0] 528 real_path = path.split('@')[0]
827 logging.info("GuessVCS(%s)" % path) 529 logging.info("DetectGit(%s)" % path)
828 # Subversion has a .svn in all working directories.
829 if os.path.isdir(os.path.join(real_path, '.svn')):
830 return SVN(options, path, file_list)
831 530
832 # Git has a command to test if you're in a git tree. 531 # Git has a command to test if you're in a git tree.
833 # Try running it, but don't die if we don't have git installed. 532 # Try running it, but don't die if we don't have git installed.
834 try: 533 try:
835 subprocess2.check_output( 534 subprocess2.check_output(
836 ['git', 'rev-parse', '--is-inside-work-tree'], cwd=real_path, 535 ['git', 'rev-parse', '--is-inside-work-tree'], cwd=real_path,
837 stderr=subprocess2.VOID) 536 stderr=subprocess2.VOID)
838 return GIT(options, path, file_list) 537 return GIT(options, path, file_list)
839 except OSError, e: 538 except OSError, e:
840 if e.errno != errno.ENOENT: 539 if e.errno != errno.ENOENT:
841 raise 540 raise
842 except subprocess2.CalledProcessError, e: 541 except subprocess2.CalledProcessError, e:
843 if e.returncode != errno.ENOENT and e.returncode != 128: 542 if e.returncode != errno.ENOENT and e.returncode != 128:
844 # ENOENT == 2 = they don't have git installed. 543 # ENOENT == 2 = they don't have git installed.
845 # 128 = git error code when not in a repo. 544 # 128 = git error code when not in a repo.
846 logging.warning('Unexpected error code: %s' % e.returncode) 545 logging.warning('Unexpected error code: %s' % e.returncode)
847 raise 546 raise
848 raise NoTryServerAccess( 547 return None
849 ( 'Could not guess version control system for %s.\n'
850 'Are you in a working copy directory?') % path)
851 548
852 549
853 def GetMungedDiff(path_diff, diff): 550 def GetMungedDiff(path_diff, diff):
854 # Munge paths to match svn. 551 # Munge paths to match svn.
855 changed_files = [] 552 changed_files = []
856 for i in range(len(diff)): 553 for i in range(len(diff)):
857 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): 554 if diff[i].startswith('--- ') or diff[i].startswith('+++ '):
858 new_file = posixpath.join(path_diff, diff[i][4:]).replace('\\', '/') 555 new_file = posixpath.join(path_diff, diff[i][4:]).replace('\\', '/')
859 if diff[i].startswith('--- '): 556 if diff[i].startswith('--- '):
860 file_path = new_file.split('\t')[0].strip() 557 file_path = new_file.split('\t')[0].strip()
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
971 help=("Disable automatic search for gclient or repo " 668 help=("Disable automatic search for gclient or repo "
972 "checkout root.")) 669 "checkout root."))
973 group.add_option("-E", "--exclude", action="append", 670 group.add_option("-E", "--exclude", action="append",
974 default=['ChangeLog'], metavar='REGEXP', 671 default=['ChangeLog'], metavar='REGEXP',
975 help="Regexp patterns to exclude files. Default: %default") 672 help="Regexp patterns to exclude files. Default: %default")
976 group.add_option("--upstream_branch", action="store", 673 group.add_option("--upstream_branch", action="store",
977 help="Specify the upstream branch to diff against in the " 674 help="Specify the upstream branch to diff against in the "
978 "main checkout") 675 "main checkout")
979 parser.add_option_group(group) 676 parser.add_option_group(group)
980 677
981 group = optparse.OptionGroup(parser, "Access the try server by HTTP")
982 group.add_option("--use_http",
983 action="store_const",
984 const=_SendChangeHTTP,
985 dest="send_patch",
986 help="Use HTTP to talk to the try server [default]")
987 group.add_option("-H", "--host",
988 help="Host address")
989 group.add_option("-P", "--port", type="int",
990 help="HTTP port")
991 parser.add_option_group(group)
992
993 group = optparse.OptionGroup(parser, "Access the try server with SVN") 678 group = optparse.OptionGroup(parser, "Access the try server with SVN")
994 group.add_option("--use_svn",
995 action="store_const",
996 const=_SendChangeSVN,
997 dest="send_patch",
998 help="Use SVN to talk to the try server")
999 group.add_option("-S", "--svn_repo", 679 group.add_option("-S", "--svn_repo",
1000 metavar="SVN_URL", 680 metavar="SVN_URL",
1001 help="SVN url to use to write the changes in; --use_svn is " 681 help="SVN url to use to write the changes in; --use_svn is "
1002 "implied when using --svn_repo") 682 "implied when using --svn_repo")
1003 parser.add_option_group(group) 683 parser.add_option_group(group)
1004 684
1005 group = optparse.OptionGroup(parser, "Access the try server with Git")
1006 group.add_option("--use_git",
1007 action="store_const",
1008 const=_SendChangeGit,
1009 dest="send_patch",
1010 help="Use GIT to talk to the try server")
1011 group.add_option("-G", "--git_repo",
1012 metavar="GIT_URL",
1013 help="GIT url to use to write the changes in; --use_git is "
1014 "implied when using --git_repo")
1015 parser.add_option_group(group)
1016
1017 group = optparse.OptionGroup(parser, "Access the try server with Gerrit")
1018 group.add_option("--use_gerrit",
1019 action="store_const",
1020 const=_SendChangeGerrit,
1021 dest="send_patch",
1022 help="Use Gerrit to talk to the try server")
1023 group.add_option("--gerrit_url",
1024 metavar="GERRIT_URL",
1025 help="Gerrit url to post a tryjob to; --use_gerrit is "
1026 "implied when using --gerrit_url")
1027 parser.add_option_group(group)
1028
1029 return parser 685 return parser
1030 686
1031 687
1032 def TryChange(argv, 688 def TryChange_Git(argv,
1033 change,
1034 swallow_exception, 689 swallow_exception,
1035 prog=None, 690 prog=None,
1036 extra_epilog=None): 691 extra_epilog=None):
1037 """ 692 """
1038 Args: 693 Args:
1039 argv: Arguments and options. 694 argv: Arguments and options.
1040 change: Change instance corresponding to the CL. 695 change: Change instance corresponding to the CL.
1041 swallow_exception: Whether we raise or swallow exceptions. 696 swallow_exception: Whether we raise or swallow exceptions.
1042 """ 697 """
1043 parser = gen_parser(prog) 698 parser = gen_parser(prog)
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1092 options.rietveld_url = match.group(1) 747 options.rietveld_url = match.group(1)
1093 748
1094 try: 749 try:
1095 changed_files = None 750 changed_files = None
1096 # Always include os.getcwd() in the checkout settings. 751 # Always include os.getcwd() in the checkout settings.
1097 path = os.getcwd() 752 path = os.getcwd()
1098 753
1099 file_list = [] 754 file_list = []
1100 if options.files: 755 if options.files:
1101 file_list = options.files 756 file_list = options.files
1102 elif change: 757 elif (not options.email or not options.diff or not options.revision
758 or options.revision.lower() == 'auto'):
759 changelist = git_cl.Changelist()
760 change = changelist.GetChange(changelist.GetUpstreamBranch(), None)
1103 file_list = [f.LocalPath() for f in change.AffectedFiles()] 761 file_list = [f.LocalPath() for f in change.AffectedFiles()]
1104 762
1105 if options.upstream_branch: 763 if options.upstream_branch:
1106 path += '@' + options.upstream_branch 764 path += '@' + options.upstream_branch
1107 # Clear file list so that the correct list will be retrieved from the 765 # Clear file list so that the correct list will be retrieved from the
1108 # upstream branch. 766 # upstream branch.
1109 file_list = [] 767 file_list = []
1110 768
1111 current_vcs = GuessVCS(options, path, file_list) 769 detected_git = DetectGit(options, path, file_list)
1112 current_vcs.AutomagicalSettings() 770 if detected_git:
1113 options = current_vcs.options 771 detected_git.AutomagicalSettings()
1114 vcs_is_git = type(current_vcs) is GIT 772 options = detected_git.options
1115
1116 # So far, git_repo doesn't work with SVN
1117 if options.git_repo and not vcs_is_git:
1118 parser.error('--git_repo option is supported only for GIT repositories')
1119 773
1120 # If revision==auto, resolve it 774 # If revision==auto, resolve it
1121 if options.revision and options.revision.lower() == 'auto': 775 if options.revision and options.revision.lower() == 'auto':
1122 if not vcs_is_git:
1123 parser.error('--revision=auto is supported only for GIT repositories')
1124 options.revision = scm.GIT.Capture( 776 options.revision = scm.GIT.Capture(
1125 ['rev-parse', current_vcs.diff_against], 777 ['rev-parse', detected_git.diff_against],
1126 cwd=path) 778 cwd=path)
1127 779
1128 checkouts = [current_vcs] 780 checkouts = []
781 if detected_git:
782 checkouts.append(detected_git)
1129 for item in options.sub_rep: 783 for item in options.sub_rep:
1130 # Pass file_list=None because we don't know the sub repo's file list. 784 # Pass file_list=None because we don't know the sub repo's file list.
1131 checkout = GuessVCS(options, 785 checkout = DetectGit(options,
1132 os.path.join(current_vcs.checkout_root, item), 786 os.path.join(detected_git.checkout_root, item),
1133 None) 787 None)
1134 if checkout.checkout_root in [c.checkout_root for c in checkouts]: 788 if checkout.checkout_root in [c.checkout_root for c in checkouts]:
1135 parser.error('Specified the root %s two times.' % 789 parser.error('Specified the root %s two times.' %
1136 checkout.checkout_root) 790 checkout.checkout_root)
1137 checkouts.append(checkout) 791 checkouts.append(checkout)
1138 792
1139 can_http = options.port and options.host 793 if not options.svn_repo:
1140 can_svn = options.svn_repo 794 parser.error('A SVN repo is required to send the job.')
1141 can_git = options.git_repo
1142 can_gerrit = options.gerrit_url
1143 can_something = can_http or can_svn or can_git or can_gerrit
1144 # If there was no transport selected yet, now we must have enough data to
1145 # select one.
1146 if not options.send_patch and not can_something:
1147 parser.error('Please specify an access method.')
1148 795
1149 # Convert options.diff into the content of the diff. 796 # Convert options.diff into the content of the diff.
1150 if options.url: 797 if options.url:
1151 if options.files: 798 if options.files:
1152 parser.error('You cannot specify files and --url at the same time.') 799 parser.error('You cannot specify files and --url at the same time.')
1153 options.diff = urllib2.urlopen(options.url).read() 800 options.diff = urllib2.urlopen(options.url).read()
1154 elif options.diff: 801 elif options.diff:
1155 if options.files: 802 if options.files:
1156 parser.error('You cannot specify files and --diff at the same time.') 803 parser.error('You cannot specify files and --diff at the same time.')
1157 options.diff = gclient_utils.FileRead(options.diff, 'rb') 804 options.diff = gclient_utils.FileRead(options.diff, 'rb')
1158 elif options.issue and options.patchset is None: 805 elif options.issue and options.patchset is None:
1159 # Retrieve the patch from rietveld when the diff is not specified. 806 # Retrieve the patch from rietveld when the diff is not specified.
1160 # When patchset is specified, it's because it's done by gcl/git-try. 807 # When patchset is specified, it's because it's done by gcl/git-try.
1161 api_url = '%s/api/%d' % (options.rietveld_url, options.issue) 808 api_url = '%s/api/%d' % (options.rietveld_url, options.issue)
1162 logging.debug(api_url) 809 logging.debug(api_url)
1163 contents = json.loads(urllib2.urlopen(api_url).read()) 810 contents = json.loads(urllib2.urlopen(api_url).read())
1164 options.patchset = contents['patchsets'][-1] 811 options.patchset = contents['patchsets'][-1]
1165 diff_url = ('%s/download/issue%d_%d.diff' % 812 diff_url = ('%s/download/issue%d_%d.diff' %
1166 (options.rietveld_url, options.issue, options.patchset)) 813 (options.rietveld_url, options.issue, options.patchset))
1167 diff = GetMungedDiff('', urllib2.urlopen(diff_url).readlines()) 814 diff = GetMungedDiff('', urllib2.urlopen(diff_url).readlines())
1168 options.diff = ''.join(diff[0]) 815 options.diff = ''.join(diff[0])
1169 changed_files = diff[1] 816 changed_files = diff[1]
1170 else: 817 elif checkouts:
1171 # Use this as the base. 818 # Use this as the base.
1172 root = checkouts[0].checkout_root 819 root = checkouts[0].checkout_root
1173 diffs = [] 820 diffs = []
1174 for checkout in checkouts: 821 for checkout in checkouts:
1175 raw_diff = checkout.GenerateDiff() 822 raw_diff = checkout.GenerateDiff()
1176 if not raw_diff: 823 if not raw_diff:
1177 continue 824 continue
1178 diff = raw_diff.splitlines(True) 825 diff = raw_diff.splitlines(True)
1179 path_diff = gclient_utils.PathDifference(root, checkout.checkout_root) 826 path_diff = gclient_utils.PathDifference(root, checkout.checkout_root)
1180 # Munge it. 827 # Munge it.
1181 diffs.extend(GetMungedDiff(path_diff, diff)[0]) 828 diffs.extend(GetMungedDiff(path_diff, diff)[0])
1182 if not diffs: 829 if not diffs:
1183 logging.error('Empty or non-existant diff, exiting.') 830 logging.error('Empty or non-existant diff, exiting.')
1184 return 1 831 return 1
1185 options.diff = ''.join(diffs) 832 options.diff = ''.join(diffs)
833 else:
834 raise RuntimeError('Nothing to send')
1186 835
1187 if not options.name: 836 if not options.name:
1188 if options.issue: 837 if options.issue:
1189 options.name = 'Issue %s' % options.issue 838 options.name = 'Issue %s' % options.issue
1190 else: 839 else:
1191 options.name = 'Unnamed' 840 options.name = 'Unnamed'
1192 print('Note: use --name NAME to change the try job name.') 841 print('Note: use --name NAME to change the try job name.')
1193 842
1194 if not options.email: 843 if not options.email:
1195 parser.error('Using an anonymous checkout. Please use --email or set ' 844 parser.error('Using an anonymous checkout. Please use --email or set '
(...skipping 18 matching lines...) Expand all
1214 863
1215 if options.print_bots: 864 if options.print_bots:
1216 print 'Bots which would be used:' 865 print 'Bots which would be used:'
1217 for bot in bot_spec: 866 for bot in bot_spec:
1218 if bot[1]: 867 if bot[1]:
1219 print ' %s:%s' % (bot[0], ','.join(bot[1])) 868 print ' %s:%s' % (bot[0], ','.join(bot[1]))
1220 else: 869 else:
1221 print ' %s' % (bot[0]) 870 print ' %s' % (bot[0])
1222 return 0 871 return 0
1223 872
1224 # Determine sending protocol 873 _SendChangeSVN(bot_spec, options)
1225 if options.send_patch:
1226 # If forced.
1227 senders = [options.send_patch]
1228 else:
1229 # Try sending patch using avaialble protocols
1230 all_senders = [
1231 (_SendChangeHTTP, can_http),
1232 (_SendChangeSVN, can_svn),
1233 (_SendChangeGerrit, can_gerrit),
1234 (_SendChangeGit, can_git),
1235 ]
1236 senders = [sender for sender, can in all_senders if can]
1237
1238 # Send the patch.
1239 for sender in senders:
1240 try:
1241 sender(bot_spec, options)
1242 return 0
1243 except NoTryServerAccess:
1244 is_last = sender == senders[-1]
1245 if is_last:
1246 raise
1247 assert False, "Unreachable code"
1248 except Error, e: 874 except Error, e:
1249 if swallow_exception: 875 if swallow_exception:
1250 return 1 876 return 1
1251 print >> sys.stderr, e 877 print >> sys.stderr, e
1252 return 1 878 return 1
1253 except (gclient_utils.Error, subprocess2.CalledProcessError), e: 879 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
1254 print >> sys.stderr, e 880 print >> sys.stderr, e
1255 return 1 881 return 1
1256 return 0 882 return 0
1257
1258
1259 if __name__ == "__main__":
1260 fix_encoding.fix_encoding()
1261 sys.exit(TryChange(None, None, False))
OLDNEW
« no previous file with comments | « tests/trychange_git_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698