OLD | NEW |
---|---|
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 import optparse | 6 import optparse |
7 import os | 7 import os |
8 import re | 8 import re |
9 import string | 9 import string |
10 import sys | 10 import sys |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
65 command = "%s %s" % (gcl_path, subcommand) | 65 command = "%s %s" % (gcl_path, subcommand) |
66 return os.system(command) | 66 return os.system(command) |
67 | 67 |
68 def gclUpload(revision, author): | 68 def gclUpload(revision, author): |
69 command = ("upload " + str(revision) + | 69 command = ("upload " + str(revision) + |
70 " --send_mail --no_presubmit --reviewers=" + author) | 70 " --send_mail --no_presubmit --reviewers=" + author) |
71 return runGcl(command) | 71 return runGcl(command) |
72 | 72 |
73 def getSVNInfo(url, revision): | 73 def getSVNInfo(url, revision): |
74 info = {} | 74 info = {} |
75 try: | 75 svn_info = subprocess2.capture( |
76 svn_info = subprocess2.check_output( | 76 ['svn', 'info', '--non-interactive', '%s@%s' % (url, revision)], |
77 ['svn', 'info', '%s@%s' % (url, revision)]).splitlines() | 77 stderr=subprocess2.VOID).splitlines() |
78 for line in svn_info: | 78 for line in svn_info: |
79 match = re.search(r"(.*?):(.*)", line) | 79 match = re.search(r"(.*?):(.*)", line) |
80 if match: | 80 if match: |
81 info[match.group(1).strip()] = match.group(2).strip() | 81 info[match.group(1).strip()] = match.group(2).strip() |
82 except subprocess2.CalledProcessError: | |
83 pass | |
84 return info | 82 return info |
85 | 83 |
86 def isSVNDirty(): | 84 def isSVNDirty(): |
87 svn_status = subprocess2.check_output(['svn', 'status']).splitlines() | 85 svn_status = subprocess2.check_output(['svn', 'status']).splitlines() |
88 for line in svn_status: | 86 for line in svn_status: |
89 match = re.search(r"^[^X?]", line) | 87 match = re.search(r"^[^X?]", line) |
90 if match: | 88 if match: |
91 return True | 89 return True |
92 | 90 |
93 return False | 91 return False |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
484 return default | 482 return default |
485 return answer | 483 return answer |
486 | 484 |
487 | 485 |
488 def drover(options, args): | 486 def drover(options, args): |
489 revision = options.revert or options.merge | 487 revision = options.revert or options.merge |
490 | 488 |
491 # Initialize some variables used below. They can be overwritten by | 489 # Initialize some variables used below. They can be overwritten by |
492 # the drover.properties file. | 490 # the drover.properties file. |
493 BASE_URL = "svn://svn.chromium.org/chrome" | 491 BASE_URL = "svn://svn.chromium.org/chrome" |
492 REVERT_ALT_URLS = ['svn://svn.chromium.org/chrome-internal', | |
493 'svn://svn.chromium.org/native_client'] | |
494 TRUNK_URL = BASE_URL + "/trunk/src" | 494 TRUNK_URL = BASE_URL + "/trunk/src" |
495 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 495 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
496 SKIP_CHECK_WORKING = True | 496 SKIP_CHECK_WORKING = True |
497 PROMPT_FOR_AUTHOR = False | 497 PROMPT_FOR_AUTHOR = False |
498 | 498 |
499 # Translate a given milestone to the appropriate branch number. | 499 # Translate a given milestone to the appropriate branch number. |
500 if options.milestone: | 500 if options.milestone: |
501 options.branch = getBranchForMilestone(options.milestone) | 501 options.branch = getBranchForMilestone(options.milestone) |
502 if not options.branch: | 502 if not options.branch: |
503 return 1 | 503 return 1 |
(...skipping 10 matching lines...) Expand all Loading... | |
514 global file_pattern_ | 514 global file_pattern_ |
515 if os.path.exists("drover.properties"): | 515 if os.path.exists("drover.properties"): |
516 FILE_PATTERN = file_pattern_ | 516 FILE_PATTERN = file_pattern_ |
517 f = open("drover.properties") | 517 f = open("drover.properties") |
518 exec(f) | 518 exec(f) |
519 f.close() | 519 f.close() |
520 if FILE_PATTERN: | 520 if FILE_PATTERN: |
521 file_pattern_ = FILE_PATTERN | 521 file_pattern_ = FILE_PATTERN |
522 | 522 |
523 if options.revert and options.branch: | 523 if options.revert and options.branch: |
524 print 'Note: --branch is usually not needed for reverts.' | |
524 url = BRANCH_URL.replace("$branch", options.branch) | 525 url = BRANCH_URL.replace("$branch", options.branch) |
525 elif options.merge and options.sbranch: | 526 elif options.merge and options.sbranch: |
526 url = BRANCH_URL.replace("$branch", options.sbranch) | 527 url = BRANCH_URL.replace("$branch", options.sbranch) |
527 elif options.revert and options.url: | 528 elif options.revert: |
528 url = options.url | 529 url = options.url or BASE_URL |
529 file_pattern_ = r"[ ]+([MADUC])[ ]+((/.*)/(.*))" | 530 file_pattern_ = r"[ ]+([MADUC])[ ]+((/.*)/(.*))" |
530 else: | 531 else: |
531 url = TRUNK_URL | 532 url = TRUNK_URL |
532 | 533 |
533 working = options.workdir or DEFAULT_WORKING | 534 working = options.workdir or DEFAULT_WORKING |
534 | 535 |
535 if options.local: | 536 if options.local: |
536 working = os.getcwd() | 537 working = os.getcwd() |
537 if not inCheckoutRoot(working): | 538 if not inCheckoutRoot(working): |
538 print "'%s' appears not to be the root of a working copy" % working | 539 print "'%s' appears not to be the root of a working copy" % working |
539 return 1 | 540 return 1 |
540 if (isSVNDirty() and not | 541 if (isSVNDirty() and not |
541 prompt("Working copy contains uncommitted files. Continue?")): | 542 prompt("Working copy contains uncommitted files. Continue?")): |
542 return 1 | 543 return 1 |
543 | 544 |
545 if options.revert and not options.no_alt_urls: | |
546 for cur_url in [url] + REVERT_ALT_URLS: | |
547 try: | |
548 url_rev = int(getSVNInfo(cur_url, 'HEAD', ).get('Revision', '')) | |
549 if 0 <= url_rev - options.revert < 20000: | |
M-A Ruel
2013/03/18 17:09:37
Maybe 10000 to be on the safer side?
Isaac (away)
2013/03/18 19:25:59
I started w/ 10000 but it's only 2 months of chrom
| |
550 if cur_url != url: | |
551 print 'Guessing svn repo: %s.' % cur_url, | |
552 print 'Use --no-alt-urls to disable heuristic.' | |
553 url = cur_url | |
554 break | |
555 except ValueError: | |
556 pass | |
544 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 557 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
545 os.system(command) | 558 os.system(command) |
546 | 559 |
547 if not (options.revertbot or prompt("Is this the correct revision?")): | 560 if not (options.revertbot or prompt("Is this the correct revision?")): |
548 return 0 | 561 return 0 |
549 | 562 |
550 if (os.path.exists(working)) and not options.local: | 563 if (os.path.exists(working)) and not options.local: |
551 if not (options.revertbot or SKIP_CHECK_WORKING or | 564 if not (options.revertbot or SKIP_CHECK_WORKING or |
552 prompt("Working directory: '%s' already exists, clobber?" % working)): | 565 prompt("Working directory: '%s' already exists, clobber?" % working)): |
553 return 0 | 566 return 0 |
(...skipping 11 matching lines...) Expand all Loading... | |
565 checkoutRevision(url, revision, branch_url) | 578 checkoutRevision(url, revision, branch_url) |
566 # Merge everything that changed | 579 # Merge everything that changed |
567 mergeRevision(url, revision) | 580 mergeRevision(url, revision) |
568 # "Export" files that were added from the source and add them to branch | 581 # "Export" files that were added from the source and add them to branch |
569 exportRevision(url, revision) | 582 exportRevision(url, revision) |
570 # Delete directories that were deleted (file deletes are handled in the | 583 # Delete directories that were deleted (file deletes are handled in the |
571 # merge). | 584 # merge). |
572 deleteRevision(url, revision) | 585 deleteRevision(url, revision) |
573 elif options.revert: | 586 elif options.revert: |
574 action = "Revert" | 587 action = "Revert" |
575 if options.branch: | |
576 url = BRANCH_URL.replace("$branch", options.branch) | |
577 pop_em = not options.url | 588 pop_em = not options.url |
578 checkoutRevision(url, revision, url, True, pop_em) | 589 checkoutRevision(url, revision, url, True, pop_em) |
579 revertRevision(url, revision) | 590 revertRevision(url, revision) |
580 revertExportRevision(url, revision) | 591 revertExportRevision(url, revision) |
581 | 592 |
582 # Check the base url so we actually find the author who made the change | 593 # Check the base url so we actually find the author who made the change |
583 if options.auditor: | 594 if options.auditor: |
584 author = options.auditor | 595 author = options.auditor |
585 else: | 596 else: |
586 author = getAuthor(url, revision) | 597 author = getAuthor(url, revision) |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
665 option_parser.add_option('-s', '--sbranch', | 676 option_parser.add_option('-s', '--sbranch', |
666 help='Source branch for merge') | 677 help='Source branch for merge') |
667 option_parser.add_option('-r', '--revert', type="int", | 678 option_parser.add_option('-r', '--revert', type="int", |
668 help='Revision to revert') | 679 help='Revision to revert') |
669 option_parser.add_option('-w', '--workdir', | 680 option_parser.add_option('-w', '--workdir', |
670 help='subdir to use for the revert') | 681 help='subdir to use for the revert') |
671 option_parser.add_option('-u', '--url', | 682 option_parser.add_option('-u', '--url', |
672 help='svn url to use for the revert') | 683 help='svn url to use for the revert') |
673 option_parser.add_option('-a', '--auditor', | 684 option_parser.add_option('-a', '--auditor', |
674 help='overrides the author for reviewer') | 685 help='overrides the author for reviewer') |
675 option_parser.add_option('', '--revertbot', action='store_true', | 686 option_parser.add_option('--revertbot', action='store_true', |
676 default=False) | 687 default=False) |
677 option_parser.add_option('', '--revertbot-commit', action='store_true', | 688 option_parser.add_option('--no-alt-urls', action='store_true', |
689 help='Disable heuristics used to determine svn url') | |
690 option_parser.add_option('--revertbot-commit', action='store_true', | |
678 default=False) | 691 default=False) |
679 option_parser.add_option('', '--revertbot-reviewers') | 692 option_parser.add_option('--revertbot-reviewers') |
680 options, args = option_parser.parse_args() | 693 options, args = option_parser.parse_args() |
681 | 694 |
682 if not options.merge and not options.revert: | 695 if not options.merge and not options.revert: |
683 option_parser.error("You need at least --merge or --revert") | 696 option_parser.error("You need at least --merge or --revert") |
684 return 1 | 697 return 1 |
685 | 698 |
686 if options.merge and not (options.branch or options.milestone or | 699 if options.merge and not (options.branch or options.milestone or |
687 options.local): | 700 options.local): |
688 option_parser.error("--merge requires either --branch " | 701 option_parser.error("--merge requires either --branch " |
689 "or --milestone or --local") | 702 "or --milestone or --local") |
690 return 1 | 703 return 1 |
691 | 704 |
692 if options.local and (options.revert or options.branch or options.milestone): | 705 if options.local and (options.revert or options.branch or options.milestone): |
693 option_parser.error("--local cannot be used with --revert " | 706 option_parser.error("--local cannot be used with --revert " |
694 "or --branch or --milestone") | 707 "or --branch or --milestone") |
695 return 1 | 708 return 1 |
696 | 709 |
697 if options.branch and options.milestone: | 710 if options.branch and options.milestone: |
698 option_parser.error("--branch cannot be used with --milestone") | 711 option_parser.error("--branch cannot be used with --milestone") |
699 return 1 | 712 return 1 |
700 | 713 |
701 return drover(options, args) | 714 return drover(options, args) |
702 | 715 |
703 | 716 |
704 if __name__ == "__main__": | 717 if __name__ == "__main__": |
705 sys.exit(main()) | 718 sys.exit(main()) |
OLD | NEW |