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

Side by Side Diff: trychange.py

Issue 184343003: Now trychange can store patches in a Git repo (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 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_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 repository or by directly connecting 7 the try server by either writting to a svn/git repository or by directly
8 to the server by HTTP. 8 connecting to the server by HTTP.
9 """ 9 """
10 10
11
12 import contextlib
11 import datetime 13 import datetime
12 import errno 14 import errno
13 import getpass 15 import getpass
14 import itertools 16 import itertools
15 import json 17 import json
16 import logging 18 import logging
17 import optparse 19 import optparse
18 import os 20 import os
19 import posixpath 21 import posixpath
20 import re 22 import re
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 premade diff file on the local drive: 65 premade diff file on the local drive:
64 %(prog)s --email user@example.com 66 %(prog)s --email user@example.com
65 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff 67 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff
66 68
67 Running only on a 'mac' slave with revision 123 and clobber first; specify 69 Running only on a 'mac' slave with revision 123 and clobber first; specify
68 manually the 3 source files to use for the try job: 70 manually the 3 source files to use for the try job:
69 %(prog)s --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h 71 %(prog)s --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h
70 -f include/b.h 72 -f include/b.h
71 """ 73 """
72 74
75 GIT_PATCH_DIR_BASENAME = os.path.join('git-try', 'patches-git')
73 76
74 def DieWithError(message): 77 def DieWithError(message):
75 print >> sys.stderr, message 78 print >> sys.stderr, message
76 sys.exit(1) 79 sys.exit(1)
77 80
78 81
79 def RunCommand(args, error_ok=False, error_message=None, **kwargs): 82 def RunCommand(args, error_ok=False, error_message=None, **kwargs):
80 try: 83 try:
81 return subprocess2.check_output(args, shell=False, **kwargs) 84 return subprocess2.check_output(args, shell=False, **kwargs)
82 except subprocess2.CalledProcessError, e: 85 except subprocess2.CalledProcessError, e:
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 """Set default settings based on the gcl-style settings from the repository. 160 """Set default settings based on the gcl-style settings from the repository.
158 161
159 The settings in the self.options object will only be set if no previous 162 The settings in the self.options object will only be set if no previous
160 value exists (i.e. command line flags to the try command will override the 163 value exists (i.e. command line flags to the try command will override the
161 settings in codereview.settings). 164 settings in codereview.settings).
162 """ 165 """
163 settings = { 166 settings = {
164 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'), 167 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'),
165 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'), 168 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'),
166 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'), 169 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'),
170 'git_repo': self.GetCodeReviewSetting('TRYSERVER_GIT_URL'),
167 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'), 171 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'),
172 # primarily for revision=auto
ghost stip (do not use) 2014/03/26 19:16:36 nit: Capitalize comments and end with a period.
173 'revision': self.GetCodeReviewSetting('TRYSERVER_REVISION'),
168 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'), 174 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'),
169 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'), 175 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'),
170 } 176 }
171 logging.info('\n'.join(['%s: %s' % (k, v) 177 logging.info('\n'.join(['%s: %s' % (k, v)
172 for (k, v) in settings.iteritems() if v])) 178 for (k, v) in settings.iteritems() if v]))
173 for (k, v) in settings.iteritems(): 179 for (k, v) in settings.iteritems():
174 # Avoid overwriting options already set using command line flags. 180 # Avoid overwriting options already set using command line flags.
175 if v and getattr(self.options, k) is None: 181 if v and getattr(self.options, k) is None:
176 setattr(self.options, k, v) 182 setattr(self.options, k, v)
177 183
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 285
280 The files in the list should either be absolute paths or relative to the 286 The files in the list should either be absolute paths or relative to the
281 given root. 287 given root.
282 """ 288 """
283 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True, 289 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True,
284 revision=self.diff_against) 290 revision=self.diff_against)
285 291
286 292
287 class GIT(SCM): 293 class GIT(SCM):
288 """Gathers the options and diff for a git checkout.""" 294 """Gathers the options and diff for a git checkout."""
295
289 def __init__(self, *args, **kwargs): 296 def __init__(self, *args, **kwargs):
290 SCM.__init__(self, *args, **kwargs) 297 SCM.__init__(self, *args, **kwargs)
291 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) 298 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
292 if not self.options.name: 299 if not self.options.name:
293 self.options.name = scm.GIT.GetPatchName(self.checkout_root) 300 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
294 if not self.options.email: 301 if not self.options.email:
295 self.options.email = scm.GIT.GetEmail(self.checkout_root) 302 self.options.email = scm.GIT.GetEmail(self.checkout_root)
296 if not self.diff_against: 303 if not self.diff_against:
297 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root) 304 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root)
298 if not self.diff_against: 305 if not self.diff_against:
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
403 410
404 bot_spec.extend(_ApplyTestFilter(options.testfilter, new_style)) 411 bot_spec.extend(_ApplyTestFilter(options.testfilter, new_style))
405 412
406 except ImportError: 413 except ImportError:
407 pass 414 pass
408 415
409 return bot_spec 416 return bot_spec
410 417
411 418
412 def _ParseSendChangeOptions(bot_spec, options): 419 def _ParseSendChangeOptions(bot_spec, options):
413 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" 420 """Parse common options passed to _SendChangeHTTP, _SendChangeSVN and
421 _SendChangeGit.
422 """
414 values = [ 423 values = [
415 ('user', options.user), 424 ('user', options.user),
416 ('name', options.name), 425 ('name', options.name),
417 ] 426 ]
418 if options.email: 427 if options.email:
419 values.append(('email', options.email)) 428 values.append(('email', options.email))
420 if options.revision: 429 if options.revision:
421 values.append(('revision', options.revision)) 430 values.append(('revision', options.revision))
422 if options.clobber: 431 if options.clobber:
423 values.append(('clobber', 'true')) 432 values.append(('clobber', 'true'))
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url, 482 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url,
474 str(e.args))) 483 str(e.args)))
475 if not connection: 484 if not connection:
476 raise NoTryServerAccess('%s is unaccessible.' % url) 485 raise NoTryServerAccess('%s is unaccessible.' % url)
477 logging.info('Reading response...') 486 logging.info('Reading response...')
478 response = connection.read() 487 response = connection.read()
479 logging.info('Done') 488 logging.info('Done')
480 if response != 'OK': 489 if response != 'OK':
481 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response)) 490 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response))
482 491
492 @contextlib.contextmanager
493 def _TempFilename(name, contents=None):
494 """Create a temporary directory, append the specified name and yield.
M-A Ruel 2014/03/26 12:57:10 Imperative verb tense means "Creates" instead of "
nodir 2014/03/26 17:18:17 A lot of methods in this file uses non-imperative
495
496 In contrast to NamedTemporaryFile, does not keep the file open.
497 Deletes the file on __exit__.
498 """
499 temp_dir = tempfile.mkdtemp(prefix=name)
500 try:
501 path = os.path.join(temp_dir, name)
502 if contents:
503 with open(path, 'w') as f:
504 f.write(contents)
505 yield path
506 finally:
507 shutil.rmtree(temp_dir, True)
508
509
510 @contextlib.contextmanager
511 def _PrepareDescriptionAndPatchFiles(description, options):
512 """Create temporary files with description and patch, yield execution,
ghost stip (do not use) 2014/03/26 19:16:36 nit: """One line with short desciption < 80 chars.
513 and delete the files.
514
515
M-A Ruel 2014/03/26 12:57:10 remove one line
nodir 2014/03/26 17:18:17 Done.
516 __enter__ called on the return value returns a tuple of patch_filename and
517 description_filename.
518
519 Args:
520 description: contents of description file.
521 options: patchset options object. Must have attributes: user,
522 name (of patch) and diff (contents of patch).
523 """
524 current_time = str(datetime.datetime.now()).replace(':', '.')
525 patch_basename = '%s.%s.%s.diff' % (Escape(options.user),
526 Escape(options.name), current_time)
527 with _TempFilename('description', description) as description_filename:
528 with _TempFilename(patch_basename, options.diff) as patch_filename:
529 yield patch_filename, description_filename
530
483 531
484 def _SendChangeSVN(bot_spec, options): 532 def _SendChangeSVN(bot_spec, options):
485 """Send a change to the try server by committing a diff file on a subversion 533 """Send a change to the try server by committing a diff file on a subversion
486 server.""" 534 server."""
487 if not options.svn_repo: 535 if not options.svn_repo:
488 raise NoTryServerAccess('Please use the --svn_repo option to specify the' 536 raise NoTryServerAccess('Please use the --svn_repo option to specify the'
489 ' try server svn repository to connect to.') 537 ' try server svn repository to connect to.')
490 538
491 values = _ParseSendChangeOptions(bot_spec, options) 539 values = _ParseSendChangeOptions(bot_spec, options)
492 description = ''.join("%s=%s\n" % (k, v) for k, v in values) 540 description = ''.join("%s=%s\n" % (k, v) for k, v in values)
493 logging.info('Sending by SVN') 541 logging.info('Sending by SVN')
494 logging.info(description) 542 logging.info(description)
495 logging.info(options.svn_repo) 543 logging.info(options.svn_repo)
496 logging.info(options.diff) 544 logging.info(options.diff)
497 if options.dry_run: 545 if options.dry_run:
498 return 546 return
499 547
500 # Create a temporary directory, put a uniquely named file in it with the diff 548 with _PrepareDescriptionAndPatchFiles(description, options) as (
501 # content and svn import that. 549 patch_filename, description_filename):
502 temp_dir = tempfile.mkdtemp() 550 if sys.platform == "cygwin":
M-A Ruel 2014/03/26 12:57:10 This file uses single quotes, please keep it consi
nodir 2014/03/26 17:18:17 There is a lot of double quotes in this file. I wi
503 temp_file = tempfile.NamedTemporaryFile() 551 # Small chromium-specific issue here:
504 try: 552 # git-try uses /usr/bin/python on cygwin but svn.bat will be used
553 # instead of /usr/bin/svn by default. That causes bad things(tm) since
554 # Windows' svn.exe has no clue about cygwin paths. Hence force to use
555 # the cygwin version in this particular context.
556 exe = "/usr/bin/svn"
557 else:
558 exe = "svn"
559 patch_dir = os.path.dirname(patch_filename)
560 command = [exe, 'import', '-q', patch_dir, options.svn_repo, '--file',
561 description_filename]
562 if scm.SVN.AssertVersion("1.5")[0]:
563 command.append('--no-ignore')
564
505 try: 565 try:
506 # Description
507 temp_file.write(description)
508 temp_file.flush()
509
510 # Diff file
511 current_time = str(datetime.datetime.now()).replace(':', '.')
512 file_name = (Escape(options.user) + '.' + Escape(options.name) +
513 '.%s.diff' % current_time)
514 full_path = os.path.join(temp_dir, file_name)
515 with open(full_path, 'wb') as f:
516 f.write(options.diff)
517
518 # Committing it will trigger a try job.
519 if sys.platform == "cygwin":
520 # Small chromium-specific issue here:
521 # git-try uses /usr/bin/python on cygwin but svn.bat will be used
522 # instead of /usr/bin/svn by default. That causes bad things(tm) since
523 # Windows' svn.exe has no clue about cygwin paths. Hence force to use
524 # the cygwin version in this particular context.
525 exe = "/usr/bin/svn"
526 else:
527 exe = "svn"
528 command = [exe, 'import', '-q', temp_dir, options.svn_repo, '--file',
529 temp_file.name]
530 if scm.SVN.AssertVersion("1.5")[0]:
531 command.append('--no-ignore')
532
533 subprocess2.check_call(command) 566 subprocess2.check_call(command)
534 except subprocess2.CalledProcessError, e: 567 except subprocess2.CalledProcessError, e:
535 raise NoTryServerAccess(str(e)) 568 raise NoTryServerAccess(str(e))
536 finally: 569
537 temp_file.close() 570
538 shutil.rmtree(temp_dir, True) 571 def _GetPatchGitRepo(git_url):
572 """Get a path to a Git repo with patches associated with the given url.
573
574 Store patches in .git/git-try/patches-git directory, a git repo. If it
575 doesn't exist yet or its origin URL is different, then clean up and clone it.
576 If it existed before, then pull changes.
577
578 Does not support SVN repo.
579
580 Returns a path to the directory with patches.
581 """
582 git_dir = scm.GIT.GetGitDir(os.getcwd())
583 patch_dir = os.path.join(git_dir, GIT_PATCH_DIR_BASENAME)
584
585 logging.info('Looking for git repo for patches')
586 # Is there already a repo with the expected url or should we clone?
587 clone = True
588 if os.path.exists(patch_dir) and scm.GIT.IsInsideWorkTree(patch_dir):
589 existing_url = scm.GIT.Capture(
590 ['config', '--local', 'remote.origin.url'],
591 cwd=patch_dir)
592 clone = existing_url != git_url
593
594 if clone:
595 if os.path.exists(patch_dir):
596 logging.info('Cleaning up')
597 shutil.rmtree(patch_dir, True)
598 logging.info('Cloning patch repo')
599 scm.GIT.Capture(['clone', git_url, GIT_PATCH_DIR_BASENAME], cwd=git_dir)
agable 2014/03/26 17:15:10 The patch-specific refs are going to be non-branch
600 email = scm.GIT.GetEmail(cwd=os.getcwd())
601 scm.GIT.Capture(['config', '--local', 'user.email', email], patch_dir)
602 else:
603 if scm.GIT.IsWorkTreeDirty(patch_dir):
604 logging.info('Work dir is dirty: hard reset!')
605 scm.GIT.Capture(['reset', '--hard'], cwd=patch_dir)
606 logging.info('Updating patch repo')
607 scm.GIT.Capture(['pull', 'origin', 'master'], cwd=patch_dir)
M-A Ruel 2014/03/26 12:57:10 One issue I see here is that do you want the user
agable 2014/03/26 17:15:10 I think the plan is for origin/master to contain a
nodir 2014/03/26 17:18:17 I have a CL locally that does something similar:
608
609 return patch_dir
610
611
612 def _SendChangeGit(bot_spec, options):
613 """Send a change to the try server by committing a diff file to a GIT repo"""
614 if not options.git_repo:
615 raise NoTryServerAccess('Please use the --git_repo option to specify the '
616 'try server git repository to connect to.')
617
618 values = _ParseSendChangeOptions(bot_spec, options)
619 comment_subject = '%s.%s' % (options.user, options.name)
620 comment_body = ''.join("%s=%s\n" % (k, v) for k, v in values)
621 description = '%s\n\n%s' % (comment_subject, comment_body)
622 logging.info('Sending by GIT')
623 logging.info(description)
624 logging.info(options.git_repo)
625 logging.info(options.diff)
626 if options.dry_run:
627 return
628
629 patch_dir = _GetPatchGitRepo(options.git_repo)
630 assert scm.GIT.IsInsideWorkTree(patch_dir)
631 assert not scm.GIT.IsWorkTreeDirty(patch_dir)
632
633 with _PrepareDescriptionAndPatchFiles(description, options) as (
634 patch_filename, description_filename):
635 logging.info('Committing patch')
636 target_filename = os.path.abspath(
637 os.path.join(patch_dir, os.path.basename(patch_filename)))
638 shutil.copyfile(patch_filename, target_filename)
639 try:
agable 2014/03/26 17:15:10 This whole block needs to be changed to commit the
640 scm.GIT.Capture(['add', target_filename], cwd=patch_dir)
641 scm.GIT.Capture(['commit', '-F', description_filename], cwd=patch_dir)
642 assert not scm.GIT.IsWorkTreeDirty(patch_dir)
643 logging.info('Pushing patch')
644 scm.GIT.Capture(['push', 'origin', 'master'], cwd=patch_dir)
M-A Ruel 2014/03/26 12:57:10 This is problematic, since it creates a race condi
nodir 2014/03/26 17:18:17 See above.
645 except subprocess2.CalledProcessError, e:
646 scm.GIT.Capture(['reset', '--hard', 'origin/master'], cwd=patch_dir)
647 raise NoTryServerAccess(str(e))
648
539 649
540 650
541 def PrintSuccess(bot_spec, options): 651 def PrintSuccess(bot_spec, options):
542 if not options.dry_run: 652 if not options.dry_run:
543 text = 'Patch \'%s\' sent to try server' % options.name 653 text = 'Patch \'%s\' sent to try server' % options.name
544 if bot_spec: 654 if bot_spec:
545 text += ': %s' % ', '.join( 655 text += ': %s' % ', '.join(
546 '%s:%s' % (b[0], ','.join(b[1])) for b in bot_spec) 656 '%s:%s' % (b[0], ','.join(b[1])) for b in bot_spec)
547 print(text) 657 print(text)
548 658
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 "times to specify multiple builders. ex: " 754 "times to specify multiple builders. ex: "
645 "'-bwin_rel:ui_tests,webkit_unit_tests -bwin_layout'. See " 755 "'-bwin_rel:ui_tests,webkit_unit_tests -bwin_layout'. See "
646 "the try server waterfall for the builders name and the tests " 756 "the try server waterfall for the builders name and the tests "
647 "available. Can also be used to specify gtest_filter, e.g. " 757 "available. Can also be used to specify gtest_filter, e.g. "
648 "-bwin_rel:base_unittests:ValuesTest.*Value")) 758 "-bwin_rel:base_unittests:ValuesTest.*Value"))
649 group.add_option("-B", "--print_bots", action="store_true", 759 group.add_option("-B", "--print_bots", action="store_true",
650 help="Print bots we would use (e.g. from PRESUBMIT.py)" 760 help="Print bots we would use (e.g. from PRESUBMIT.py)"
651 " and exit. Do not send patch. Like --dry_run" 761 " and exit. Do not send patch. Like --dry_run"
652 " but less verbose.") 762 " but less verbose.")
653 group.add_option("-r", "--revision", 763 group.add_option("-r", "--revision",
654 help="Revision to use for the try job; default: the " 764 help="Revision to use for the try job. If 'auto' is "
765 "specified, it is resolved to the revision a patch is "
766 "generated against (Git only). Default: the "
655 "revision will be determined by the try server; see " 767 "revision will be determined by the try server; see "
656 "its waterfall for more info") 768 "its waterfall for more info")
657 group.add_option("-c", "--clobber", action="store_true", 769 group.add_option("-c", "--clobber", action="store_true",
658 help="Force a clobber before building; e.g. don't do an " 770 help="Force a clobber before building; e.g. don't do an "
659 "incremental build") 771 "incremental build")
660 # TODO(maruel): help="Select a specific configuration, usually 'debug' or " 772 # TODO(maruel): help="Select a specific configuration, usually 'debug' or "
661 # "'release'" 773 # "'release'"
662 group.add_option("--target", help=optparse.SUPPRESS_HELP) 774 group.add_option("--target", help=optparse.SUPPRESS_HELP)
663 775
664 group.add_option("--project", 776 group.add_option("--project",
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
730 group.add_option("--use_svn", 842 group.add_option("--use_svn",
731 action="store_const", 843 action="store_const",
732 const=_SendChangeSVN, 844 const=_SendChangeSVN,
733 dest="send_patch", 845 dest="send_patch",
734 help="Use SVN to talk to the try server") 846 help="Use SVN to talk to the try server")
735 group.add_option("-S", "--svn_repo", 847 group.add_option("-S", "--svn_repo",
736 metavar="SVN_URL", 848 metavar="SVN_URL",
737 help="SVN url to use to write the changes in; --use_svn is " 849 help="SVN url to use to write the changes in; --use_svn is "
738 "implied when using --svn_repo") 850 "implied when using --svn_repo")
739 parser.add_option_group(group) 851 parser.add_option_group(group)
852
853 group = optparse.OptionGroup(parser, "Access the try server with Git")
854 group.add_option("--use_git",
855 action="store_const",
856 const=_SendChangeGit,
857 dest="send_patch",
858 help="Use GIT to talk to the try server")
859 group.add_option("-G", "--git_repo",
860 metavar="GIT_URL",
861 help="GIT url to use to write the changes in; --use_git is "
862 "implied when using --git_repo")
863 parser.add_option_group(group)
740 return parser 864 return parser
741 865
742 866
743 def TryChange(argv, 867 def TryChange(argv,
744 change, 868 change,
745 swallow_exception, 869 swallow_exception,
746 prog=None, 870 prog=None,
747 extra_epilog=None): 871 extra_epilog=None):
748 """ 872 """
749 Args: 873 Args:
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
798 match = re.match(r'^(.*)/(\d+)/?$', options.rietveld_url) 922 match = re.match(r'^(.*)/(\d+)/?$', options.rietveld_url)
799 if match: 923 if match:
800 if options.issue or options.patchset: 924 if options.issue or options.patchset:
801 parser.error('Cannot use both --issue and use a review number url') 925 parser.error('Cannot use both --issue and use a review number url')
802 options.issue = int(match.group(2)) 926 options.issue = int(match.group(2))
803 options.rietveld_url = match.group(1) 927 options.rietveld_url = match.group(1)
804 928
805 try: 929 try:
806 changed_files = None 930 changed_files = None
807 # Always include os.getcwd() in the checkout settings. 931 # Always include os.getcwd() in the checkout settings.
808 checkouts = []
809 path = os.getcwd() 932 path = os.getcwd()
810 933
811 file_list = [] 934 file_list = []
812 if options.files: 935 if options.files:
813 file_list = options.files 936 file_list = options.files
814 elif change: 937 elif change:
815 file_list = [f.LocalPath() for f in change.AffectedFiles()] 938 file_list = [f.LocalPath() for f in change.AffectedFiles()]
816 939
817 if options.upstream_branch: 940 if options.upstream_branch:
818 path += '@' + options.upstream_branch 941 path += '@' + options.upstream_branch
819 # Clear file list so that the correct list will be retrieved from the 942 # Clear file list so that the correct list will be retrieved from the
820 # upstream branch. 943 # upstream branch.
821 file_list = [] 944 file_list = []
822 checkouts.append(GuessVCS(options, path, file_list)) 945
823 checkouts[0].AutomagicalSettings() 946 current_vcs = GuessVCS(options, path, file_list)
947 current_vcs.AutomagicalSettings()
948 options = current_vcs.options
949 vcs_is_git = type(current_vcs) is GIT
950
951 # So far, git_repo doesn't work with SVN
952 if options.git_repo and not vcs_is_git:
953 parser.error('--git_repo option is supported only for GIT repositories')
954
955 # If revision==auto, resolve it
956 if options.revision and options.revision.lower() == 'auto':
957 if not vcs_is_git:
958 parser.error('--revision=auto is supported only for GIT repositories')
959 options.revision = scm.GIT.Capture(
960 ['rev-parse', current_vcs.diff_against],
961 cwd=path)
962
963 checkouts = [current_vcs]
824 for item in options.sub_rep: 964 for item in options.sub_rep:
825 # Pass file_list=None because we don't know the sub repo's file list. 965 # Pass file_list=None because we don't know the sub repo's file list.
826 checkout = GuessVCS(options, 966 checkout = GuessVCS(options,
827 os.path.join(checkouts[0].checkout_root, item), 967 os.path.join(current_vcs.checkout_root, item),
828 None) 968 None)
829 if checkout.checkout_root in [c.checkout_root for c in checkouts]: 969 if checkout.checkout_root in [c.checkout_root for c in checkouts]:
830 parser.error('Specified the root %s two times.' % 970 parser.error('Specified the root %s two times.' %
831 checkout.checkout_root) 971 checkout.checkout_root)
832 checkouts.append(checkout) 972 checkouts.append(checkout)
833 973
834 can_http = options.port and options.host 974 can_http = options.port and options.host
835 can_svn = options.svn_repo 975 can_svn = options.svn_repo
976 can_git = options.git_repo
836 # If there was no transport selected yet, now we must have enough data to 977 # If there was no transport selected yet, now we must have enough data to
837 # select one. 978 # select one.
838 if not options.send_patch and not (can_http or can_svn): 979 if not options.send_patch and not (can_http or can_svn or can_git):
839 parser.error('Please specify an access method.') 980 parser.error('Please specify an access method.')
840 981
841 # Convert options.diff into the content of the diff. 982 # Convert options.diff into the content of the diff.
842 if options.url: 983 if options.url:
843 if options.files: 984 if options.files:
844 parser.error('You cannot specify files and --url at the same time.') 985 parser.error('You cannot specify files and --url at the same time.')
845 options.diff = urllib2.urlopen(options.url).read() 986 options.diff = urllib2.urlopen(options.url).read()
846 elif options.diff: 987 elif options.diff:
847 if options.files: 988 if options.files:
848 parser.error('You cannot specify files and --diff at the same time.') 989 parser.error('You cannot specify files and --diff at the same time.')
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
906 1047
907 if options.print_bots: 1048 if options.print_bots:
908 print 'Bots which would be used:' 1049 print 'Bots which would be used:'
909 for bot in bot_spec: 1050 for bot in bot_spec:
910 if bot[1]: 1051 if bot[1]:
911 print ' %s:%s' % (bot[0], ','.join(bot[1])) 1052 print ' %s:%s' % (bot[0], ','.join(bot[1]))
912 else: 1053 else:
913 print ' %s' % (bot[0]) 1054 print ' %s' % (bot[0])
914 return 0 1055 return 0
915 1056
916 # Send the patch. 1057 # Determine sending protocol
917 if options.send_patch: 1058 if options.send_patch:
918 # If forced. 1059 # If forced.
919 options.send_patch(bot_spec, options) 1060 senders = [options.send_patch]
920 PrintSuccess(bot_spec, options) 1061 else:
921 return 0 1062 # Try sending patch using avaialble protocols
922 try: 1063 all_senders = [
923 if can_http: 1064 (_SendChangeHTTP, can_http),
924 _SendChangeHTTP(bot_spec, options) 1065 (_SendChangeSVN, can_svn),
1066 (_SendChangeGit, can_git)
1067 ]
1068 senders = [sender for sender, can in all_senders if can]
1069
1070 # Send the patch.
1071 for sender in senders:
1072 try:
1073 sender(bot_spec, options)
925 PrintSuccess(bot_spec, options) 1074 PrintSuccess(bot_spec, options)
926 return 0 1075 return 0
927 except NoTryServerAccess: 1076 except NoTryServerAccess:
928 if not can_svn: 1077 is_last = sender == senders[-1]
929 raise 1078 if is_last:
930 _SendChangeSVN(bot_spec, options) 1079 raise
931 PrintSuccess(bot_spec, options) 1080 assert False, "Unreachable code"
932 return 0
933 except (InvalidScript, NoTryServerAccess), e: 1081 except (InvalidScript, NoTryServerAccess), e:
934 if swallow_exception: 1082 if swallow_exception:
935 return 1 1083 return 1
936 print >> sys.stderr, e 1084 print >> sys.stderr, e
937 return 1 1085 return 1
938 except (gclient_utils.Error, subprocess2.CalledProcessError), e: 1086 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
939 print >> sys.stderr, e 1087 print >> sys.stderr, e
940 return 1 1088 return 1
941 return 0 1089 return 0
942 1090
943 1091
944 if __name__ == "__main__": 1092 if __name__ == "__main__":
945 fix_encoding.fix_encoding() 1093 fix_encoding.fix_encoding()
946 sys.exit(TryChange(None, None, False)) 1094 sys.exit(TryChange(None, None, False))
OLDNEW
« no previous file with comments | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698