| 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 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 7 | 7 |
| 8 """A git-command for integrating reviews on Rietveld.""" | 8 """A git-command for integrating reviews on Rietveld.""" |
| 9 | 9 |
| 10 from distutils.version import LooseVersion | 10 from distutils.version import LooseVersion |
| 11 from multiprocessing.pool import ThreadPool | 11 from multiprocessing.pool import ThreadPool |
| 12 import base64 | 12 import base64 |
| 13 import collections |
| 13 import glob | 14 import glob |
| 14 import httplib | 15 import httplib |
| 15 import json | 16 import json |
| 16 import logging | 17 import logging |
| 17 import optparse | 18 import optparse |
| 18 import os | 19 import os |
| 19 import Queue | 20 import Queue |
| 20 import re | 21 import re |
| 21 import stat | 22 import stat |
| 22 import sys | 23 import sys |
| (...skipping 1486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1509 else len(branches_to_fetch)) | 1510 else len(branches_to_fetch)) |
| 1510 for x in pool.imap_unordered(fetch, branches_to_fetch): | 1511 for x in pool.imap_unordered(fetch, branches_to_fetch): |
| 1511 yield x | 1512 yield x |
| 1512 else: | 1513 else: |
| 1513 # Do not use GetApprovingReviewers(), since it requires an HTTP request. | 1514 # Do not use GetApprovingReviewers(), since it requires an HTTP request. |
| 1514 for b in branches: | 1515 for b in branches: |
| 1515 cl = Changelist(branchref=b, auth_config=auth_config) | 1516 cl = Changelist(branchref=b, auth_config=auth_config) |
| 1516 url = cl.GetIssueURL() | 1517 url = cl.GetIssueURL() |
| 1517 yield (b, url, 'waiting' if url else 'error') | 1518 yield (b, url, 'waiting' if url else 'error') |
| 1518 | 1519 |
| 1520 |
| 1521 def upload_branch_deps(cl, args): |
| 1522 """Uploads CLs of local branches that are dependents of the current branch. |
| 1523 |
| 1524 If the local branch dependency tree looks like: |
| 1525 test1 -> test2.1 -> test3.1 |
| 1526 -> test3.2 |
| 1527 -> test2.2 -> test3.3 |
| 1528 |
| 1529 and you run "git cl upload --dependencies" from test1 then "git cl upload" is |
| 1530 run on the dependent branches in this order: |
| 1531 test2.1, test3.1, test3.2, test2.2, test3.3 |
| 1532 |
| 1533 Note: This function does not rebase your local dependent branches. Use it when |
| 1534 you make a change to the parent branch that will not conflict with its |
| 1535 dependent branches, and you would like their dependencies updated in |
| 1536 Rietveld. |
| 1537 """ |
| 1538 if git_common.is_dirty_git_tree('upload-branch-deps'): |
| 1539 return 1 |
| 1540 |
| 1541 root_branch = cl.GetBranch() |
| 1542 if root_branch is None: |
| 1543 DieWithError('Can\'t find dependent branches from detached HEAD state. ' |
| 1544 'Get on a branch!') |
| 1545 if not cl.GetIssue() or not cl.GetPatchset(): |
| 1546 DieWithError('Current branch does not have an uploaded CL. We cannot set ' |
| 1547 'patchset dependencies without an uploaded CL.') |
| 1548 |
| 1549 branches = RunGit(['for-each-ref', |
| 1550 '--format=%(refname:short) %(upstream:short)', |
| 1551 'refs/heads']) |
| 1552 if not branches: |
| 1553 print('No local branches found.') |
| 1554 return 0 |
| 1555 |
| 1556 # Create a dictionary of all local branches to the branches that are dependent |
| 1557 # on it. |
| 1558 tracked_to_dependents = collections.defaultdict(list) |
| 1559 for b in branches.splitlines(): |
| 1560 tokens = b.split() |
| 1561 if len(tokens) == 2: |
| 1562 branch_name, tracked = tokens |
| 1563 tracked_to_dependents[tracked].append(branch_name) |
| 1564 |
| 1565 print |
| 1566 print 'The dependent local branches of %s are:' % root_branch |
| 1567 dependents = [] |
| 1568 def traverse_dependents_preorder(branch, padding=''): |
| 1569 dependents_to_process = tracked_to_dependents.get(branch, []) |
| 1570 padding += ' ' |
| 1571 for dependent in dependents_to_process: |
| 1572 print '%s%s' % (padding, dependent) |
| 1573 dependents.append(dependent) |
| 1574 traverse_dependents_preorder(dependent, padding) |
| 1575 traverse_dependents_preorder(root_branch) |
| 1576 print |
| 1577 |
| 1578 if not dependents: |
| 1579 print 'There are no dependent local branches for %s' % root_branch |
| 1580 return 0 |
| 1581 |
| 1582 print ('This command will checkout all dependent branches and run ' |
| 1583 '"git cl upload".') |
| 1584 ask_for_data('[Press enter to continue or ctrl-C to quit]') |
| 1585 |
| 1586 # Add a default patchset title to all upload calls. |
| 1587 args.extend(['-t', 'Updated patchset dependency']) |
| 1588 # Record all dependents that failed to upload. |
| 1589 failures = {} |
| 1590 # Go through all dependents, checkout the branch and upload. |
| 1591 try: |
| 1592 for dependent_branch in dependents: |
| 1593 print |
| 1594 print '--------------------------------------' |
| 1595 print 'Running "git cl upload" from %s:' % dependent_branch |
| 1596 RunGit(['checkout', '-q', dependent_branch]) |
| 1597 print |
| 1598 try: |
| 1599 if CMDupload(OptionParser(), args) != 0: |
| 1600 print 'Upload failed for %s!' % dependent_branch |
| 1601 failures[dependent_branch] = 1 |
| 1602 except: # pylint: disable=W0702 |
| 1603 failures[dependent_branch] = 1 |
| 1604 print |
| 1605 finally: |
| 1606 # Swap back to the original root branch. |
| 1607 RunGit(['checkout', '-q', root_branch]) |
| 1608 |
| 1609 print |
| 1610 print 'Upload complete for dependent branches!' |
| 1611 for dependent_branch in dependents: |
| 1612 upload_status = 'failed' if failures.get(dependent_branch) else 'succeeded' |
| 1613 print ' %s : %s' % (dependent_branch, upload_status) |
| 1614 print |
| 1615 |
| 1616 return 0 |
| 1617 |
| 1618 |
| 1519 def CMDstatus(parser, args): | 1619 def CMDstatus(parser, args): |
| 1520 """Show status of changelists. | 1620 """Show status of changelists. |
| 1521 | 1621 |
| 1522 Colors are used to tell the state of the CL unless --fast is used: | 1622 Colors are used to tell the state of the CL unless --fast is used: |
| 1523 - Red not sent for review or broken | 1623 - Red not sent for review or broken |
| 1524 - Blue waiting for review | 1624 - Blue waiting for review |
| 1525 - Yellow waiting for you to reply to review | 1625 - Yellow waiting for you to reply to review |
| 1526 - Green LGTM'ed | 1626 - Green LGTM'ed |
| 1527 - Magenta in the commit queue | 1627 - Magenta in the commit queue |
| 1528 - Cyan was committed, branch can be deleted | 1628 - Cyan was committed, branch can be deleted |
| (...skipping 661 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2190 'Default: remote branch head, or master') | 2290 'Default: remote branch head, or master') |
| 2191 parser.add_option('--squash', action='store_true', | 2291 parser.add_option('--squash', action='store_true', |
| 2192 help='Squash multiple commits into one (Gerrit only)') | 2292 help='Squash multiple commits into one (Gerrit only)') |
| 2193 parser.add_option('--email', default=None, | 2293 parser.add_option('--email', default=None, |
| 2194 help='email address to use to connect to Rietveld') | 2294 help='email address to use to connect to Rietveld') |
| 2195 parser.add_option('--tbr-owners', dest='tbr_owners', action='store_true', | 2295 parser.add_option('--tbr-owners', dest='tbr_owners', action='store_true', |
| 2196 help='add a set of OWNERS to TBR') | 2296 help='add a set of OWNERS to TBR') |
| 2197 parser.add_option('--cq-dry-run', dest='cq_dry_run', action='store_true', | 2297 parser.add_option('--cq-dry-run', dest='cq_dry_run', action='store_true', |
| 2198 help='Send the patchset to do a CQ dry run right after ' | 2298 help='Send the patchset to do a CQ dry run right after ' |
| 2199 'upload.') | 2299 'upload.') |
| 2300 parser.add_option('--dependencies', action='store_true', |
| 2301 help='Uploads CLs of all the local branches that depend on ' |
| 2302 'the current branch') |
| 2200 | 2303 |
| 2304 orig_args = args |
| 2201 add_git_similarity(parser) | 2305 add_git_similarity(parser) |
| 2202 auth.add_auth_options(parser) | 2306 auth.add_auth_options(parser) |
| 2203 (options, args) = parser.parse_args(args) | 2307 (options, args) = parser.parse_args(args) |
| 2204 auth_config = auth.extract_auth_config_from_options(options) | 2308 auth_config = auth.extract_auth_config_from_options(options) |
| 2205 | 2309 |
| 2206 if git_common.is_dirty_git_tree('upload'): | 2310 if git_common.is_dirty_git_tree('upload'): |
| 2207 return 1 | 2311 return 1 |
| 2208 | 2312 |
| 2209 options.reviewers = cleanup_list(options.reviewers) | 2313 options.reviewers = cleanup_list(options.reviewers) |
| 2210 options.cc = cleanup_list(options.cc) | 2314 options.cc = cleanup_list(options.cc) |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2275 RunGit(['rev-parse', 'HEAD']).strip()) | 2379 RunGit(['rev-parse', 'HEAD']).strip()) |
| 2276 # Run post upload hooks, if specified. | 2380 # Run post upload hooks, if specified. |
| 2277 if settings.GetRunPostUploadHook(): | 2381 if settings.GetRunPostUploadHook(): |
| 2278 presubmit_support.DoPostUploadExecuter( | 2382 presubmit_support.DoPostUploadExecuter( |
| 2279 change, | 2383 change, |
| 2280 cl, | 2384 cl, |
| 2281 settings.GetRoot(), | 2385 settings.GetRoot(), |
| 2282 options.verbose, | 2386 options.verbose, |
| 2283 sys.stdout) | 2387 sys.stdout) |
| 2284 | 2388 |
| 2389 # Upload all dependencies if specified. |
| 2390 if options.dependencies: |
| 2391 print |
| 2392 print '--dependencies has been specified.' |
| 2393 print 'All dependent local branches will be re-uploaded.' |
| 2394 print |
| 2395 # Remove the dependencies flag from args so that we do not end up in a |
| 2396 # loop. |
| 2397 orig_args.remove('--dependencies') |
| 2398 upload_branch_deps(cl, orig_args) |
| 2285 return ret | 2399 return ret |
| 2286 | 2400 |
| 2287 | 2401 |
| 2288 def IsSubmoduleMergeCommit(ref): | 2402 def IsSubmoduleMergeCommit(ref): |
| 2289 # When submodules are added to the repo, we expect there to be a single | 2403 # When submodules are added to the repo, we expect there to be a single |
| 2290 # non-git-svn merge commit at remote HEAD with a signature comment. | 2404 # non-git-svn merge commit at remote HEAD with a signature comment. |
| 2291 pattern = '^SVN changes up to revision [0-9]*$' | 2405 pattern = '^SVN changes up to revision [0-9]*$' |
| 2292 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] | 2406 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] |
| 2293 return RunGit(cmd) != '' | 2407 return RunGit(cmd) != '' |
| 2294 | 2408 |
| (...skipping 1121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3416 if __name__ == '__main__': | 3530 if __name__ == '__main__': |
| 3417 # These affect sys.stdout so do it outside of main() to simplify mocks in | 3531 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 3418 # unit testing. | 3532 # unit testing. |
| 3419 fix_encoding.fix_encoding() | 3533 fix_encoding.fix_encoding() |
| 3420 colorama.init() | 3534 colorama.init() |
| 3421 try: | 3535 try: |
| 3422 sys.exit(main(sys.argv[1:])) | 3536 sys.exit(main(sys.argv[1:])) |
| 3423 except KeyboardInterrupt: | 3537 except KeyboardInterrupt: |
| 3424 sys.stderr.write('interrupted\n') | 3538 sys.stderr.write('interrupted\n') |
| 3425 sys.exit(1) | 3539 sys.exit(1) |
| OLD | NEW |