Chromium Code Reviews| 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 |